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 */
156 typedef enum RegistryType {
157 kObjectId = 0xc1, kRefTypeId
161 * Hash function for object IDs. Since objects are at least 8 bytes, and
162 * could someday be allocated on 16-byte boundaries, we don't want to use
163 * the low 4 bits in our hash.
165 static inline u4 registryHash(u4 val)
171 * (This is a dvmHashTableLookup() callback.)
173 static int registryCompare(const void* obj1, const void* obj2)
175 return (int) obj1 - (int) obj2;
180 * Determine if an id is already in the list.
182 * If the list doesn't yet exist, this creates it.
184 * Lock the registry before calling here.
187 static bool lookupId(ObjectId id)
191 found = dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
192 (void*)(u4) id, registryCompare, false);
195 assert(found == (void*)(u4) id);
201 * Register an object, if it hasn't already been.
203 * This is used for both ObjectId and RefTypeId. In theory we don't have
204 * to register RefTypeIds unless we're worried about classes unloading.
206 * Null references must be represented as zero, or the debugger will get
209 static ObjectId registerObject(const Object* obj, RegistryType type, bool reg)
216 assert((u4) obj != 0xcccccccc);
217 assert((u4) obj > 0x100);
219 id = (ObjectId)(u4)obj | ((u8) type) << 32;
223 dvmHashTableLock(gDvm.dbgRegistry);
224 if (!gDvm.debuggerConnected) {
225 /* debugger has detached while we were doing stuff? */
226 LOGI("ignoring registerObject request in thread=%d\n",
227 dvmThreadSelf()->threadId);
232 dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
233 (void*)(u4) id, registryCompare, true);
236 dvmHashTableUnlock(gDvm.dbgRegistry);
241 * Verify that an object has been registered. If it hasn't, the debugger
242 * is asking for something we didn't send it, which means something
243 * somewhere is broken.
245 * If speed is an issue we can encode the registry index in the high
246 * four bytes. We could also just hard-wire this to "true".
248 * Note this actually takes both ObjectId and RefTypeId.
251 static bool objectIsRegistered(ObjectId id, RegistryType type)
253 UNUSED_PARAMETER(type);
255 if (id == 0) // null reference?
258 dvmHashTableLock(gDvm.dbgRegistry);
259 bool result = lookupId(id);
260 dvmHashTableUnlock(gDvm.dbgRegistry);
266 * Convert to/from a RefTypeId.
268 * These are rarely NULL, but can be (e.g. java/lang/Object's superclass).
270 static RefTypeId classObjectToRefTypeId(ClassObject* clazz)
272 return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, true);
275 static RefTypeId classObjectToRefTypeIdNoReg(ClassObject* clazz)
277 return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, false);
280 static ClassObject* refTypeIdToClassObject(RefTypeId id)
282 assert(objectIsRegistered(id, kRefTypeId) || !gDvm.debuggerConnected);
283 return (ClassObject*)(u4) id;
287 * Convert to/from an ObjectId.
289 static ObjectId objectToObjectId(const Object* obj)
291 return registerObject(obj, kObjectId, true);
293 static ObjectId objectToObjectIdNoReg(const Object* obj)
295 return registerObject(obj, kObjectId, false);
297 static Object* objectIdToObject(ObjectId id)
299 assert(objectIsRegistered(id, kObjectId) || !gDvm.debuggerConnected);
300 return (Object*)(u4) id;
304 * Register an object ID that might not have been registered previously.
306 * Normally this wouldn't happen -- the conversion to an ObjectId would
307 * have added the object to the registry -- but in some cases (e.g.
308 * throwing exceptions) we really want to do the registration late.
310 void dvmDbgRegisterObjectId(ObjectId id)
312 Object* obj = (Object*)(u4) id;
313 LOGV("+++ registering %p (%s)\n", obj, obj->clazz->descriptor);
314 registerObject(obj, kObjectId, true);
318 * Convert to/from a MethodId.
320 * These IDs are only guaranteed unique within a class, so they could be
321 * an enumeration index. For now we just use the Method*.
323 static MethodId methodToMethodId(const Method* meth)
325 return (MethodId)(u4) meth;
327 static Method* methodIdToMethod(RefTypeId refTypeId, MethodId id)
329 // TODO? verify "id" is actually a method in "refTypeId"
330 return (Method*)(u4) id;
334 * Convert to/from a FieldId.
336 * These IDs are only guaranteed unique within a class, so they could be
337 * an enumeration index. For now we just use the Field*.
339 static FieldId fieldToFieldId(const Field* field)
341 return (FieldId)(u4) field;
343 static Field* fieldIdToField(RefTypeId refTypeId, FieldId id)
345 // TODO? verify "id" is actually a field in "refTypeId"
346 return (Field*)(u4) id;
350 * Convert to/from a FrameId.
352 * We just return a pointer to the stack frame.
354 static FrameId frameToFrameId(const void* frame)
356 return (FrameId)(u4) frame;
358 static u4* frameIdToFrame(FrameId id)
365 * Get the invocation request state.
367 DebugInvokeReq* dvmDbgGetInvokeReq()
369 return &dvmThreadSelf()->invokeReq;
373 * Enable the object registry, but don't enable debugging features yet.
375 * Only called from the JDWP handler thread.
377 void dvmDbgConnected()
379 assert(!gDvm.debuggerConnected);
381 LOGV("JDWP has attached\n");
382 assert(dvmHashTableNumEntries(gDvm.dbgRegistry) == 0);
383 gDvm.debuggerConnected = true;
387 * Enable all debugging features, including scans for breakpoints.
389 * This is a no-op if we're already active.
391 * Only called from the JDWP handler thread.
395 if (gDvm.debuggerActive)
398 LOGI("Debugger is active\n");
399 dvmInitBreakpoints();
400 gDvm.debuggerActive = true;
401 dvmEnableAllSubMode(kSubModeDebuggerActive);
402 #if defined(WITH_JIT)
403 dvmCompilerUpdateGlobalState();
408 * Disable debugging features.
410 * Set "debuggerConnected" to false, which disables use of the object
413 * Only called from the JDWP handler thread.
415 void dvmDbgDisconnected()
417 assert(gDvm.debuggerConnected);
419 gDvm.debuggerActive = false;
420 dvmDisableAllSubMode(kSubModeDebuggerActive);
421 #if defined(WITH_JIT)
422 dvmCompilerUpdateGlobalState();
425 dvmHashTableLock(gDvm.dbgRegistry);
426 gDvm.debuggerConnected = false;
428 LOGD("Debugger has detached; object registry had %d entries\n",
429 dvmHashTableNumEntries(gDvm.dbgRegistry));
431 //for (i = 0; i < gDvm.dbgRegistryNext; i++)
432 // LOGVV("%4d: 0x%llx\n", i, gDvm.dbgRegistryTable[i]);
434 dvmHashTableClear(gDvm.dbgRegistry);
435 dvmHashTableUnlock(gDvm.dbgRegistry);
439 * Returns "true" if a debugger is connected.
441 * Does not return "true" if it's just a DDM server.
443 bool dvmDbgIsDebuggerConnected()
445 return gDvm.debuggerActive;
449 * Get time since last debugger activity. Used when figuring out if the
450 * debugger has finished configuring us.
452 s8 dvmDbgLastDebuggerActivity()
454 return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
458 * JDWP thread is running, don't allow GC.
460 int dvmDbgThreadRunning()
462 ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_RUNNING);
463 return static_cast<int>(oldStatus);
467 * JDWP thread is idle, allow GC.
469 int dvmDbgThreadWaiting()
471 ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
472 return static_cast<int>(oldStatus);
476 * Restore state returned by Running/Waiting calls.
478 int dvmDbgThreadContinuing(int status)
480 ThreadStatus newStatus = static_cast<ThreadStatus>(status);
481 ThreadStatus oldStatus = dvmChangeStatus(NULL, newStatus);
482 return static_cast<int>(oldStatus);
486 * The debugger wants us to exit.
488 void dvmDbgExit(int status)
490 // TODO? invoke System.exit() to perform exit processing; ends up
491 // in System.exitInternal(), which can call JNI exit hook
492 LOGI("GC lifetime allocation: %d bytes\n", gDvm.allocProf.allocCount);
493 if (CALC_CACHE_STATS) {
494 dvmDumpAtomicCacheStats(gDvm.instanceofCache);
495 dvmDumpBootClassPath();
497 #ifdef PROFILE_FIELD_ACCESS
498 dvmDumpFieldAccessCounts();
506 * ===========================================================================
507 * Class, Object, Array
508 * ===========================================================================
512 * Get the class's type descriptor from a reference type ID.
514 const char* dvmDbgGetClassDescriptor(RefTypeId id)
518 clazz = refTypeIdToClassObject(id);
519 return clazz->descriptor;
523 * Convert a RefTypeId to an ObjectId.
525 ObjectId dvmDbgGetClassObject(RefTypeId id)
527 ClassObject* clazz = refTypeIdToClassObject(id);
528 return objectToObjectId((Object*) clazz);
532 * Return the superclass of a class (will be NULL for java/lang/Object).
534 RefTypeId dvmDbgGetSuperclass(RefTypeId id)
536 ClassObject* clazz = refTypeIdToClassObject(id);
537 return classObjectToRefTypeId(clazz->super);
541 * Return a class's defining class loader.
543 RefTypeId dvmDbgGetClassLoader(RefTypeId id)
545 ClassObject* clazz = refTypeIdToClassObject(id);
546 return objectToObjectId(clazz->classLoader);
550 * Return a class's access flags.
552 u4 dvmDbgGetAccessFlags(RefTypeId id)
554 ClassObject* clazz = refTypeIdToClassObject(id);
555 return clazz->accessFlags & JAVA_FLAGS_MASK;
559 * Is this class an interface?
561 bool dvmDbgIsInterface(RefTypeId id)
563 ClassObject* clazz = refTypeIdToClassObject(id);
564 return dvmIsInterfaceClass(clazz);
568 * dvmHashForeach callback
570 static int copyRefType(void* vclazz, void* varg)
572 RefTypeId** pRefType = (RefTypeId**)varg;
573 **pRefType = classObjectToRefTypeId((ClassObject*) vclazz);
579 * Get the complete list of reference classes (i.e. all classes except
580 * the primitive types).
582 * Returns a newly-allocated buffer full of RefTypeId values.
584 void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf)
588 dvmHashTableLock(gDvm.loadedClasses);
589 *pNumClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
590 pRefType = *pClassRefBuf =
591 (RefTypeId*)malloc(sizeof(RefTypeId) * *pNumClasses);
593 if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
594 LOGW("Warning: problem getting class list\n");
595 /* not really expecting this to happen */
597 assert(pRefType - *pClassRefBuf == (int) *pNumClasses);
600 dvmHashTableUnlock(gDvm.loadedClasses);
604 * Get the list of reference classes "visible" to the specified class
605 * loader. A class is visible to a class loader if the ClassLoader object
606 * is the defining loader or is listed as an initiating loader.
608 * Returns a newly-allocated buffer full of RefTypeId values.
610 void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
611 RefTypeId** pClassRefBuf)
614 int numClasses = 0, maxClasses;
616 classLoader = objectIdToObject(classLoaderId);
617 // I don't think classLoader can be NULL, but the spec doesn't say
619 LOGVV("GetVisibleList: comparing to %p\n", classLoader);
621 dvmHashTableLock(gDvm.loadedClasses);
623 /* over-allocate the return buffer */
624 maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
625 *pClassRefBuf = (RefTypeId*)malloc(sizeof(RefTypeId) * maxClasses);
628 * Run through the list, looking for matches.
631 for (dvmHashIterBegin(gDvm.loadedClasses, &iter); !dvmHashIterDone(&iter);
632 dvmHashIterNext(&iter))
634 ClassObject* clazz = (ClassObject*) dvmHashIterData(&iter);
636 if (clazz->classLoader == classLoader ||
637 dvmLoaderInInitiatingList(clazz, classLoader))
639 LOGVV(" match '%s'\n", clazz->descriptor);
640 (*pClassRefBuf)[numClasses++] = classObjectToRefTypeId(clazz);
643 *pNumClasses = numClasses;
645 dvmHashTableUnlock(gDvm.loadedClasses);
649 * Get the "JNI signature" for a class, e.g. "Ljava/lang/String;".
651 * Our class descriptors are in the correct format, so we just return that.
653 static const char* jniSignature(ClassObject* clazz)
655 return clazz->descriptor;
659 * Get information about a class.
661 * If "pSignature" is not NULL, *pSignature gets the "JNI signature" of
664 void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
665 const char** pSignature)
667 ClassObject* clazz = refTypeIdToClassObject(classId);
669 if (clazz->descriptor[0] == '[') {
670 /* generated array class */
671 *pStatus = CS_VERIFIED | CS_PREPARED;
672 *pTypeTag = TT_ARRAY;
674 if (clazz->status == CLASS_ERROR)
677 *pStatus = CS_VERIFIED | CS_PREPARED | CS_INITIALIZED;
678 if (dvmIsInterfaceClass(clazz))
679 *pTypeTag = TT_INTERFACE;
681 *pTypeTag = TT_CLASS;
683 if (pSignature != NULL)
684 *pSignature = jniSignature(clazz);
688 * Search the list of loaded classes for a match.
690 bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
691 RefTypeId* pRefTypeId)
695 clazz = dvmFindLoadedClass(classDescriptor);
697 *pRefTypeId = classObjectToRefTypeId(clazz);
705 * Get an object's class and "type tag".
707 void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
708 RefTypeId* pRefTypeId)
710 Object* obj = objectIdToObject(objectId);
712 if (dvmIsArrayClass(obj->clazz))
713 *pRefTypeTag = TT_ARRAY;
714 else if (dvmIsInterfaceClass(obj->clazz))
715 *pRefTypeTag = TT_INTERFACE;
717 *pRefTypeTag = TT_CLASS;
718 *pRefTypeId = classObjectToRefTypeId(obj->clazz);
722 * Get a class object's "type tag".
724 u1 dvmDbgGetClassObjectType(RefTypeId refTypeId)
726 ClassObject* clazz = refTypeIdToClassObject(refTypeId);
728 if (dvmIsArrayClass(clazz))
730 else if (dvmIsInterfaceClass(clazz))
737 * Get a class' signature.
739 const char* dvmDbgGetSignature(RefTypeId refTypeId)
743 clazz = refTypeIdToClassObject(refTypeId);
744 assert(clazz != NULL);
746 return jniSignature(clazz);
750 * Get class' source file.
752 * Returns a newly-allocated string.
754 const char* dvmDbgGetSourceFile(RefTypeId refTypeId)
758 clazz = refTypeIdToClassObject(refTypeId);
759 assert(clazz != NULL);
761 return clazz->sourceFile;
765 * Get an object's type name. (For log message display only.)
767 const char* dvmDbgGetObjectTypeName(ObjectId objectId)
772 Object* obj = objectIdToObject(objectId);
773 return jniSignature(obj->clazz);
777 * Determine whether or not a tag represents a primitive type.
779 static bool isTagPrimitive(u1 tag)
795 case JT_CLASS_OBJECT:
797 case JT_THREAD_GROUP:
798 case JT_CLASS_LOADER:
801 LOGE("ERROR: unhandled tag '%c'\n", tag);
808 * Determine the best tag type given an object's class.
810 static u1 tagFromClass(ClassObject* clazz)
812 if (dvmIsArrayClass(clazz))
815 if (clazz == gDvm.classJavaLangString) {
817 } else if (dvmIsTheClassClass(clazz)) {
818 return JT_CLASS_OBJECT;
819 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
821 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
822 return JT_THREAD_GROUP;
823 } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
824 return JT_CLASS_LOADER;
831 * Return a basic tag value based solely on a type descriptor.
833 * The ASCII value maps directly to the JDWP tag constants, so we don't
834 * need to do much here. This does not return the fancier tags like
837 static u1 basicTagFromDescriptor(const char* descriptor)
839 return descriptor[0];
843 * Objects declared to hold Object might actually hold a more specific
844 * type. The debugger may take a special interest in these (e.g. it
845 * wants to display the contents of Strings), so we want to return an
848 * Null objects are tagged JT_OBJECT.
850 static u1 tagFromObject(const Object* obj)
854 return tagFromClass(obj->clazz);
858 * Determine the tag for an object.
860 * "objectId" may be 0 (i.e. NULL reference).
862 u1 dvmDbgGetObjectTag(ObjectId objectId)
864 return tagFromObject(objectIdToObject(objectId));
868 * Get the widths of the specified JDWP.Tag value.
870 int dvmDbgGetTagWidth(int tag)
888 case JT_THREAD_GROUP:
889 case JT_CLASS_LOADER:
890 case JT_CLASS_OBJECT:
891 return sizeof(ObjectId);
896 LOGE("ERROR: unhandled tag '%c'\n", tag);
904 * Return the length of the specified array.
906 int dvmDbgGetArrayLength(ObjectId arrayId)
908 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
909 assert(dvmIsArray(arrayObj));
910 return arrayObj->length;
914 * Return a tag indicating the general type of elements in the array.
916 u1 dvmDbgGetArrayElementTag(ObjectId arrayId)
918 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
920 ClassObject* arrayClass = arrayObj->obj.clazz;
921 u1 tag = basicTagFromDescriptor(arrayClass->descriptor + 1);
922 if (!isTagPrimitive(tag)) {
923 /* try to refine it */
924 tag = tagFromClass(arrayClass->elementClass);
931 * Copy a series of values with the specified width, changing the byte
932 * ordering to big-endian.
934 static void copyValuesToBE(u1* out, const u1* in, int count, int width)
940 memcpy(out, in, count);
943 for (i = 0; i < count; i++)
944 *(((u2*) out)+i) = get2BE(in + i*2);
947 for (i = 0; i < count; i++)
948 *(((u4*) out)+i) = get4BE(in + i*4);
951 for (i = 0; i < count; i++)
952 *(((u8*) out)+i) = get8BE(in + i*8);
960 * Copy a series of values with the specified width, changing the
961 * byte order from big-endian.
963 static void copyValuesFromBE(u1* out, const u1* in, int count, int width)
969 memcpy(out, in, count);
972 for (i = 0; i < count; i++)
973 set2BE(out + i*2, *((u2*)in + i));
976 for (i = 0; i < count; i++)
977 set4BE(out + i*4, *((u4*)in + i));
980 for (i = 0; i < count; i++)
981 set8BE(out + i*8, *((u8*)in + i));
989 * Output a piece of an array to the reply buffer.
991 * Returns "false" if something looks fishy.
993 bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
996 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
997 const u1* data = (const u1*)arrayObj->contents;
1000 assert(dvmIsArray(arrayObj));
1002 if (firstIndex + count > (int)arrayObj->length) {
1003 LOGW("Request for index=%d + count=%d excceds length=%d\n",
1004 firstIndex, count, arrayObj->length);
1008 tag = basicTagFromDescriptor(arrayObj->obj.clazz->descriptor + 1);
1010 if (isTagPrimitive(tag)) {
1011 int width = dvmDbgGetTagWidth(tag);
1014 outBuf = expandBufAddSpace(pReply, count * width);
1016 copyValuesToBE(outBuf, data + firstIndex*width, count, width);
1021 pObjects = (Object**) data;
1022 pObjects += firstIndex;
1024 LOGV(" --> copying %d object IDs\n", count);
1025 //assert(tag == JT_OBJECT); // could be object or "refined" type
1027 for (i = 0; i < count; i++, pObjects++) {
1029 if (*pObjects != NULL)
1030 thisTag = tagFromObject(*pObjects);
1033 expandBufAdd1(pReply, thisTag);
1034 expandBufAddObjectId(pReply, objectToObjectId(*pObjects));
1042 * Set a range of elements in an array from the data in "buf".
1044 bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
1047 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
1048 u1* data = (u1*)arrayObj->contents;
1051 assert(dvmIsArray(arrayObj));
1053 if (firstIndex + count > (int)arrayObj->length) {
1054 LOGW("Attempt to set index=%d + count=%d excceds length=%d\n",
1055 firstIndex, count, arrayObj->length);
1059 tag = basicTagFromDescriptor(arrayObj->obj.clazz->descriptor + 1);
1061 if (isTagPrimitive(tag)) {
1062 int width = dvmDbgGetTagWidth(tag);
1064 LOGV(" --> setting %d '%c' width=%d\n", count, tag, width);
1066 copyValuesFromBE(data + firstIndex*width, buf, count, width);
1071 pObjects = (Object**) data;
1072 pObjects += firstIndex;
1074 LOGV(" --> setting %d objects", count);
1076 /* should do array type check here */
1077 for (i = 0; i < count; i++) {
1078 ObjectId id = dvmReadObjectId(&buf);
1079 *pObjects++ = objectIdToObject(id);
1087 * Create a new string.
1089 * The only place the reference will be held in the VM is in our registry.
1091 ObjectId dvmDbgCreateString(const char* str)
1093 StringObject* strObj;
1095 strObj = dvmCreateStringFromCstr(str);
1096 dvmReleaseTrackedAlloc((Object*) strObj, NULL);
1097 return objectToObjectId((Object*) strObj);
1101 * Allocate a new object of the specified type.
1103 * Add it to the registry to prevent it from being GCed.
1105 ObjectId dvmDbgCreateObject(RefTypeId classId)
1107 ClassObject* clazz = refTypeIdToClassObject(classId);
1108 Object* newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
1109 dvmReleaseTrackedAlloc(newObj, NULL);
1110 return objectToObjectId(newObj);
1114 * Allocate a new array object of the specified type and length. The
1115 * type is the array type, not the element type.
1117 * Add it to the registry to prevent it from being GCed.
1119 ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length)
1121 ClassObject* clazz = refTypeIdToClassObject(arrayTypeId);
1122 Object* newObj = (Object*) dvmAllocArrayByClass(clazz, length, ALLOC_DEFAULT);
1123 dvmReleaseTrackedAlloc(newObj, NULL);
1124 return objectToObjectId(newObj);
1128 * Determine if "instClassId" is an instance of "classId".
1130 bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId)
1132 ClassObject* instClazz = refTypeIdToClassObject(instClassId);
1133 ClassObject* clazz = refTypeIdToClassObject(classId);
1135 return dvmInstanceof(instClazz, clazz);
1140 * ===========================================================================
1142 * ===========================================================================
1146 * Get the method name from a MethodId.
1148 const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id)
1152 meth = methodIdToMethod(refTypeId, id);
1157 * Augment the access flags for synthetic methods and fields by setting
1158 * the (as described by the spec) "0xf0000000 bit". Also, strip out any
1159 * flags not specified by the Java programming language.
1161 static u4 augmentedAccessFlags(u4 accessFlags)
1163 accessFlags &= JAVA_FLAGS_MASK;
1165 if ((accessFlags & ACC_SYNTHETIC) != 0) {
1166 return accessFlags | 0xf0000000;
1173 * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
1174 * output all fields declared by the class. Inherited fields are
1177 void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
1180 ClassObject* clazz = refTypeIdToClassObject(refTypeId);
1181 assert(clazz != NULL);
1183 u4 declared = clazz->sfieldCount + clazz->ifieldCount;
1184 expandBufAdd4BE(pReply, declared);
1186 for (int i = 0; i < clazz->sfieldCount; i++) {
1187 Field* field = &clazz->sfields[i].field;
1188 expandBufAddFieldId(pReply, fieldToFieldId(field));
1189 expandBufAddUtf8String(pReply, (const u1*) field->name);
1190 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1192 static const u1 genericSignature[1] = "";
1193 expandBufAddUtf8String(pReply, genericSignature);
1195 expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
1197 for (int i = 0; i < clazz->ifieldCount; i++) {
1198 Field* field = (Field*)&clazz->ifields[i].field;
1199 expandBufAddFieldId(pReply, fieldToFieldId(field));
1200 expandBufAddUtf8String(pReply, (const u1*) field->name);
1201 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1203 static const u1 genericSignature[1] = "";
1204 expandBufAddUtf8String(pReply, genericSignature);
1206 expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
1211 * For ReferenceType.Methods and ReferenceType.MethodsWithGeneric:
1212 * output all methods declared by the class. Inherited methods are
1215 void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
1218 DexStringCache stringCache;
1219 static const u1 genericSignature[1] = "";
1225 dexStringCacheInit(&stringCache);
1227 clazz = refTypeIdToClassObject(refTypeId);
1228 assert(clazz != NULL);
1230 declared = clazz->directMethodCount + clazz->virtualMethodCount;
1231 expandBufAdd4BE(pReply, declared);
1233 for (i = 0; i < clazz->directMethodCount; i++) {
1234 meth = &clazz->directMethods[i];
1236 expandBufAddMethodId(pReply, methodToMethodId(meth));
1237 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1239 expandBufAddUtf8String(pReply,
1240 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1244 expandBufAddUtf8String(pReply, genericSignature);
1245 expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
1247 for (i = 0; i < clazz->virtualMethodCount; i++) {
1248 meth = &clazz->virtualMethods[i];
1250 expandBufAddMethodId(pReply, methodToMethodId(meth));
1251 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1253 expandBufAddUtf8String(pReply,
1254 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1258 expandBufAddUtf8String(pReply, genericSignature);
1259 expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
1262 dexStringCacheRelease(&stringCache);
1266 * Output all interfaces directly implemented by the class.
1268 void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply)
1271 int i, start, count;
1273 clazz = refTypeIdToClassObject(refTypeId);
1274 assert(clazz != NULL);
1276 if (clazz->super == NULL)
1279 start = clazz->super->iftableCount;
1281 count = clazz->iftableCount - start;
1282 expandBufAdd4BE(pReply, count);
1283 for (i = start; i < clazz->iftableCount; i++) {
1284 ClassObject* iface = clazz->iftable[i].clazz;
1285 expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
1289 typedef struct DebugCallbackContext {
1292 // used by locals table
1294 } DebugCallbackContext;
1296 static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
1298 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1300 expandBufAdd8BE(pContext->pReply, address);
1301 expandBufAdd4BE(pContext->pReply, lineNum);
1302 pContext->numItems++;
1308 * For Method.LineTable: output the line table.
1310 * Note we operate in Dalvik's 16-bit units rather than bytes.
1312 void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
1317 DebugCallbackContext context;
1319 memset (&context, 0, sizeof(DebugCallbackContext));
1321 method = methodIdToMethod(refTypeId, methodId);
1322 if (dvmIsNativeMethod(method)) {
1327 end = dvmGetMethodInsnsSize(method);
1330 expandBufAdd8BE(pReply, start);
1331 expandBufAdd8BE(pReply, end);
1333 // Add numLines later
1334 size_t numLinesOffset = expandBufGetLength(pReply);
1335 expandBufAdd4BE(pReply, 0);
1337 context.pReply = pReply;
1339 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1340 dvmGetMethodCode(method),
1341 method->clazz->descriptor,
1342 method->prototype.protoIdx,
1343 method->accessFlags,
1344 lineTablePositionsCb, NULL, &context);
1346 set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
1350 * Eclipse appears to expect that the "this" reference is in slot zero.
1351 * If it's not, the "variables" display will show two copies of "this",
1352 * possibly because it gets "this" from SF.ThisObject and then displays
1353 * all locals with nonzero slot numbers.
1355 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
1356 * SF.GetValues / SF.SetValues we map them back.
1358 static int tweakSlot(int slot, const char* name)
1362 if (strcmp(name, "this") == 0) // only remap "this" ptr
1364 else if (slot == 0) // always remap slot 0
1365 newSlot = kSlot0Sub;
1367 LOGV("untweak: %d to %d\n", slot, newSlot);
1372 * Reverse Eclipse hack.
1374 static int untweakSlot(int slot, const void* framePtr)
1378 if (slot == kSlot0Sub) {
1380 } else if (slot == 0) {
1381 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
1382 const Method* method = saveArea->method;
1383 newSlot = method->registersSize - method->insSize;
1386 LOGV("untweak: %d to %d\n", slot, newSlot);
1390 static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
1391 u4 endAddress, const char *name, const char *descriptor,
1392 const char *signature)
1394 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1396 reg = (u2) tweakSlot(reg, name);
1398 LOGV(" %2d: %d(%d) '%s' '%s' slot=%d\n",
1399 pContext->numItems, startAddress, endAddress - startAddress,
1400 name, descriptor, reg);
1402 expandBufAdd8BE(pContext->pReply, startAddress);
1403 expandBufAddUtf8String(pContext->pReply, (const u1*)name);
1404 expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
1405 if (pContext->withGeneric) {
1406 expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
1408 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
1409 expandBufAdd4BE(pContext->pReply, reg);
1411 pContext->numItems++;
1415 * For Method.VariableTable[WithGeneric]: output information about local
1416 * variables for the specified method.
1418 void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
1419 bool withGeneric, ExpandBuf* pReply)
1422 DebugCallbackContext context;
1424 memset (&context, 0, sizeof(DebugCallbackContext));
1426 method = methodIdToMethod(refTypeId, methodId);
1428 expandBufAdd4BE(pReply, method->insSize);
1430 // Add numLocals later
1431 size_t numLocalsOffset = expandBufGetLength(pReply);
1432 expandBufAdd4BE(pReply, 0);
1434 context.pReply = pReply;
1435 context.withGeneric = withGeneric;
1436 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1437 dvmGetMethodCode(method),
1438 method->clazz->descriptor,
1439 method->prototype.protoIdx,
1440 method->accessFlags,
1441 NULL, variableTableCb, &context);
1443 set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
1447 * Get the basic tag for an instance field.
1449 u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId)
1451 Object* obj = objectIdToObject(objId);
1452 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1453 const Field* field = fieldIdToField(classId, fieldId);
1454 return basicTagFromDescriptor(field->signature);
1458 * Get the basic tag for a static field.
1460 u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId)
1462 const Field* field = fieldIdToField(refTypeId, fieldId);
1463 return basicTagFromDescriptor(field->signature);
1468 * Copy the value of a static field into the output buffer, preceded
1469 * by an appropriate tag. The tag is based on the value held by the
1470 * field, not the field's type.
1472 void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply)
1474 Object* obj = objectIdToObject(objectId);
1475 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1476 InstField* ifield = (InstField*) fieldIdToField(classId, fieldId);
1477 u1 tag = basicTagFromDescriptor(ifield->field.signature);
1479 if (tag == JT_ARRAY || tag == JT_OBJECT) {
1480 Object* objVal = dvmGetFieldObject(obj, ifield->byteOffset);
1481 tag = tagFromObject(objVal);
1482 expandBufAdd1(pReply, tag);
1483 expandBufAddObjectId(pReply, objectToObjectId(objVal));
1484 LOGV(" --> ifieldId %x --> tag '%c' %p\n", fieldId, tag, objVal);
1488 LOGV(" --> ifieldId %x --> tag '%c'\n", fieldId, tag);
1489 expandBufAdd1(pReply, tag);
1493 expandBufAdd1(pReply, dvmGetFieldBoolean(obj, ifield->byteOffset));
1496 expandBufAdd1(pReply, dvmGetFieldByte(obj, ifield->byteOffset));
1499 expandBufAdd2BE(pReply, dvmGetFieldShort(obj, ifield->byteOffset));
1502 expandBufAdd2BE(pReply, dvmGetFieldChar(obj, ifield->byteOffset));
1505 expandBufAdd4BE(pReply, dvmGetFieldInt(obj, ifield->byteOffset));
1508 value.f = dvmGetFieldInt(obj, ifield->byteOffset);
1509 expandBufAdd4BE(pReply, value.i);
1512 expandBufAdd8BE(pReply, dvmGetFieldLong(obj, ifield->byteOffset));
1515 value.d = dvmGetFieldInt(obj, ifield->byteOffset);
1516 expandBufAdd8BE(pReply, value.j);
1519 LOGE("ERROR: unhandled field type '%s'\n", ifield->field.signature);
1527 * Set the value of the specified field.
1529 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
1532 Object* obj = objectIdToObject(objectId);
1533 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1534 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1536 switch (field->field.signature[0]) {
1539 dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
1543 dvmSetFieldInt(obj, field->byteOffset, value);
1548 dvmSetFieldInt(obj, field->byteOffset, value);
1553 dvmSetFieldInt(obj, field->byteOffset, value);
1557 assert(width == sizeof(ObjectId));
1558 dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
1563 dvmSetFieldLong(obj, field->byteOffset, value);
1566 LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
1573 * Copy the value of a static field into the output buffer, preceded
1574 * by an appropriate tag. The tag is based on the value held by the
1575 * field, not the field's type.
1577 void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1580 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1581 u1 tag = basicTagFromDescriptor(sfield->field.signature);
1583 if (tag == JT_ARRAY || tag == JT_OBJECT) {
1584 Object* objVal = dvmGetStaticFieldObject(sfield);
1585 tag = tagFromObject(objVal);
1586 expandBufAdd1(pReply, tag);
1587 expandBufAddObjectId(pReply, objectToObjectId(objVal));
1588 LOGV(" --> sfieldId %x --> tag '%c' %p\n", fieldId, tag, objVal);
1592 LOGV(" --> sfieldId %x --> tag '%c'\n", fieldId, tag);
1593 expandBufAdd1(pReply, tag);
1597 expandBufAdd1(pReply, dvmGetStaticFieldBoolean(sfield));
1600 expandBufAdd1(pReply, dvmGetStaticFieldByte(sfield));
1603 expandBufAdd2BE(pReply, dvmGetStaticFieldShort(sfield));
1606 expandBufAdd2BE(pReply, dvmGetStaticFieldChar(sfield));
1609 expandBufAdd4BE(pReply, dvmGetStaticFieldInt(sfield));
1612 value.f = dvmGetStaticFieldFloat(sfield);
1613 expandBufAdd4BE(pReply, value.i);
1616 expandBufAdd8BE(pReply, dvmGetStaticFieldLong(sfield));
1619 value.d = dvmGetStaticFieldDouble(sfield);
1620 expandBufAdd8BE(pReply, value.j);
1623 LOGE("ERROR: unhandled field type '%s'\n", sfield->field.signature);
1631 * Set the value of a static field.
1633 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1634 u8 rawValue, int width)
1636 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1642 switch (sfield->field.signature[0]) {
1645 dvmSetStaticFieldBoolean(sfield, value.z);
1649 dvmSetStaticFieldByte(sfield, value.b);
1653 dvmSetStaticFieldShort(sfield, value.s);
1657 dvmSetStaticFieldChar(sfield, value.c);
1661 dvmSetStaticFieldInt(sfield, value.i);
1665 dvmSetStaticFieldFloat(sfield, value.f);
1669 assert(width == sizeof(ObjectId));
1670 objVal = objectIdToObject(rawValue);
1671 dvmSetStaticFieldObject(sfield, objVal);
1675 dvmSetStaticFieldLong(sfield, value.j);
1679 dvmSetStaticFieldDouble(sfield, value.d);
1682 LOGE("ERROR: unhandled class type '%s'\n", sfield->field.signature);
1689 * Convert a string object to a UTF-8 string.
1691 * Returns a newly-allocated string.
1693 char* dvmDbgStringToUtf8(ObjectId strId)
1695 StringObject* strObj = (StringObject*) objectIdToObject(strId);
1697 return dvmCreateCstrFromString(strObj);
1702 * ===========================================================================
1703 * Thread and ThreadGroup
1704 * ===========================================================================
1708 * Convert a thread object to a Thread ptr.
1710 * This currently requires running through the list of threads and finding
1713 * IMPORTANT: grab gDvm.threadListLock before calling here.
1715 static Thread* threadObjToThread(Object* threadObj)
1719 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1720 if (thread->threadObj == threadObj)
1728 * Get the status and suspend state of a thread.
1730 bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
1735 bool result = false;
1737 threadObj = objectIdToObject(threadId);
1738 assert(threadObj != NULL);
1740 /* lock the thread list, so the thread doesn't vanish while we work */
1741 dvmLockThreadList(NULL);
1743 thread = threadObjToThread(threadObj);
1747 switch (thread->status) {
1748 case THREAD_ZOMBIE: *pThreadStatus = TS_ZOMBIE; break;
1749 case THREAD_RUNNING: *pThreadStatus = TS_RUNNING; break;
1750 case THREAD_TIMED_WAIT: *pThreadStatus = TS_SLEEPING; break;
1751 case THREAD_MONITOR: *pThreadStatus = TS_MONITOR; break;
1752 case THREAD_WAIT: *pThreadStatus = TS_WAIT; break;
1753 case THREAD_INITIALIZING: *pThreadStatus = TS_ZOMBIE; break;
1754 case THREAD_STARTING: *pThreadStatus = TS_ZOMBIE; break;
1755 case THREAD_NATIVE: *pThreadStatus = TS_RUNNING; break;
1756 case THREAD_VMWAIT: *pThreadStatus = TS_WAIT; break;
1757 case THREAD_SUSPENDED: *pThreadStatus = TS_RUNNING; break;
1760 *pThreadStatus = THREAD_ZOMBIE;
1764 if (dvmIsSuspended(thread))
1765 *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
1767 *pSuspendStatus = 0;
1772 dvmUnlockThreadList();
1777 * Get the thread's suspend count.
1779 u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
1785 threadObj = objectIdToObject(threadId);
1786 assert(threadObj != NULL);
1788 /* lock the thread list, so the thread doesn't vanish while we work */
1789 dvmLockThreadList(NULL);
1791 thread = threadObjToThread(threadObj);
1795 result = thread->suspendCount;
1798 dvmUnlockThreadList();
1803 * Determine whether or not a thread exists in the VM's thread list.
1805 * Returns "true" if the thread exists.
1807 bool dvmDbgThreadExists(ObjectId threadId)
1813 threadObj = objectIdToObject(threadId);
1814 assert(threadObj != NULL);
1816 /* lock the thread list, so the thread doesn't vanish while we work */
1817 dvmLockThreadList(NULL);
1819 thread = threadObjToThread(threadObj);
1825 dvmUnlockThreadList();
1830 * Determine whether or not a thread is suspended.
1832 * Returns "false" if the thread is running or doesn't exist.
1834 bool dvmDbgIsSuspended(ObjectId threadId)
1838 bool result = false;
1840 threadObj = objectIdToObject(threadId);
1841 assert(threadObj != NULL);
1843 /* lock the thread list, so the thread doesn't vanish while we work */
1844 dvmLockThreadList(NULL);
1846 thread = threadObjToThread(threadObj);
1850 result = dvmIsSuspended(thread);
1853 dvmUnlockThreadList();
1858 * Return the ObjectId for the "system" thread group.
1860 ObjectId dvmDbgGetSystemThreadGroupId()
1862 Object* groupObj = dvmGetSystemThreadGroup();
1863 return objectToObjectId(groupObj);
1867 * Return the ObjectId for the "main" thread group.
1869 ObjectId dvmDbgGetMainThreadGroupId()
1871 Object* groupObj = dvmGetMainThreadGroup();
1872 return objectToObjectId(groupObj);
1876 * Get the name of a thread.
1878 * Returns a newly-allocated string.
1880 char* dvmDbgGetThreadName(ObjectId threadId)
1883 StringObject* nameStr;
1887 threadObj = objectIdToObject(threadId);
1888 assert(threadObj != NULL);
1890 nameStr = (StringObject*) dvmGetFieldObject(threadObj,
1891 gDvm.offJavaLangThread_name);
1892 str = dvmCreateCstrFromString(nameStr);
1893 result = (char*) malloc(strlen(str) + 20);
1895 /* lock the thread list, so the thread doesn't vanish while we work */
1896 dvmLockThreadList(NULL);
1897 Thread* thread = threadObjToThread(threadObj);
1899 sprintf(result, "<%d> %s", thread->threadId, str);
1901 sprintf(result, "%s", str);
1902 dvmUnlockThreadList();
1909 * Get a thread's group.
1911 ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
1916 threadObj = objectIdToObject(threadId);
1917 assert(threadObj != NULL);
1919 group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
1920 return objectToObjectId(group);
1925 * Get the name of a thread group.
1927 * Returns a newly-allocated string.
1929 char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
1931 Object* threadGroup;
1932 StringObject* nameStr;
1934 threadGroup = objectIdToObject(threadGroupId);
1935 assert(threadGroup != NULL);
1937 nameStr = (StringObject*)
1938 dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_name);
1939 return dvmCreateCstrFromString(nameStr);
1943 * Get the parent of a thread group.
1945 * Returns a newly-allocated string.
1947 ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
1949 Object* threadGroup;
1952 threadGroup = objectIdToObject(threadGroupId);
1953 assert(threadGroup != NULL);
1955 parent = dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_parent);
1956 return objectToObjectId(parent);
1960 * Get the list of threads in the thread group.
1962 * We do this by running through the full list of threads and returning
1963 * the ones that have the ThreadGroup object as their owner.
1965 * If threadGroupId is set to "kAllThreads", we ignore the group field and
1966 * return all threads.
1968 * The caller must free "*ppThreadIds".
1970 void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
1971 ObjectId** ppThreadIds, u4* pThreadCount)
1973 Object* targetThreadGroup = NULL;
1977 if (threadGroupId != THREAD_GROUP_ALL) {
1978 targetThreadGroup = objectIdToObject(threadGroupId);
1979 assert(targetThreadGroup != NULL);
1982 dvmLockThreadList(NULL);
1984 thread = gDvm.threadList;
1986 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1989 /* Skip over the JDWP support thread. Some debuggers
1990 * get bent out of shape when they can't suspend and
1991 * query all threads, so it's easier if we just don't
1992 * tell them about us.
1994 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
1997 /* This thread is currently being created, and isn't ready
1998 * to be seen by the debugger yet.
2000 if (thread->threadObj == NULL)
2003 group = dvmGetFieldObject(thread->threadObj,
2004 gDvm.offJavaLangThread_group);
2005 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2009 *pThreadCount = count;
2012 *ppThreadIds = NULL;
2015 ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
2017 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2020 /* Skip over the JDWP support thread. Some debuggers
2021 * get bent out of shape when they can't suspend and
2022 * query all threads, so it's easier if we just don't
2023 * tell them about us.
2025 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2028 /* This thread is currently being created, and isn't ready
2029 * to be seen by the debugger yet.
2031 if (thread->threadObj == NULL)
2034 group = dvmGetFieldObject(thread->threadObj,
2035 gDvm.offJavaLangThread_group);
2036 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2038 *ptr++ = objectToObjectId(thread->threadObj);
2046 dvmUnlockThreadList();
2052 * The caller must free "*ppThreadIds".
2054 void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
2056 dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
2061 * Count up the #of frames on the thread's stack.
2063 * Returns -1 on failure.
2065 int dvmDbgGetThreadFrameCount(ObjectId threadId)
2071 threadObj = objectIdToObject(threadId);
2073 dvmLockThreadList(NULL);
2074 thread = threadObjToThread(threadObj);
2075 if (thread != NULL) {
2076 count = dvmComputeExactFrameDepth(thread->interpSave.curFrame);
2078 dvmUnlockThreadList();
2084 * Get info for frame N from the specified thread's stack.
2086 bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
2094 threadObj = objectIdToObject(threadId);
2096 dvmLockThreadList(NULL);
2098 thread = threadObjToThread(threadObj);
2102 framePtr = thread->interpSave.curFrame;
2104 while (framePtr != NULL) {
2105 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2106 const Method* method = saveArea->method;
2108 if (!dvmIsBreakFrame((u4*)framePtr)) {
2110 *pFrameId = frameToFrameId(framePtr);
2111 if (dvmIsInterfaceClass(method->clazz))
2112 pLoc->typeTag = TT_INTERFACE;
2114 pLoc->typeTag = TT_CLASS;
2115 pLoc->classId = classObjectToRefTypeId(method->clazz);
2116 pLoc->methodId = methodToMethodId(method);
2117 if (dvmIsNativeMethod(method))
2120 pLoc->idx = saveArea->xtra.currentPc - method->insns;
2121 dvmUnlockThreadList();
2128 framePtr = saveArea->prevFrame;
2132 dvmUnlockThreadList();
2137 * Get the ThreadId for the current thread.
2139 ObjectId dvmDbgGetThreadSelfId()
2141 Thread* self = dvmThreadSelf();
2142 return objectToObjectId(self->threadObj);
2148 void dvmDbgSuspendVM(bool isEvent)
2150 dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
2156 void dvmDbgResumeVM()
2158 dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
2162 * Suspend one thread (not ourselves).
2164 void dvmDbgSuspendThread(ObjectId threadId)
2166 Object* threadObj = objectIdToObject(threadId);
2169 dvmLockThreadList(NULL);
2171 thread = threadObjToThread(threadObj);
2172 if (thread == NULL) {
2173 /* can happen if our ThreadDeath notify crosses in the mail */
2174 LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
2176 dvmSuspendThread(thread);
2179 dvmUnlockThreadList();
2183 * Resume one thread (not ourselves).
2185 void dvmDbgResumeThread(ObjectId threadId)
2187 Object* threadObj = objectIdToObject(threadId);
2190 dvmLockThreadList(NULL);
2192 thread = threadObjToThread(threadObj);
2193 if (thread == NULL) {
2194 LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
2196 dvmResumeThread(thread);
2199 dvmUnlockThreadList();
2203 * Suspend ourselves after sending an event to the debugger.
2205 void dvmDbgSuspendSelf()
2207 dvmSuspendSelf(true);
2211 * Get the "this" object for the specified frame.
2213 static Object* getThisObject(const u4* framePtr)
2215 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2216 const Method* method = saveArea->method;
2217 int argOffset = method->registersSize - method->insSize;
2220 if (method == NULL) {
2221 /* this is a "break" frame? */
2226 LOGVV(" Pulling this object for frame at %p\n", framePtr);
2227 LOGVV(" Method='%s' native=%d static=%d this=%p\n",
2228 method->name, dvmIsNativeMethod(method),
2229 dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
2232 * No "this" pointer for statics. No args on the interp stack for
2233 * native methods invoked directly from the VM.
2235 if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
2238 thisObj = (Object*) framePtr[argOffset];
2240 if (thisObj != NULL && !dvmIsValidObject(thisObj)) {
2241 LOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL\n",
2242 framePtr, method->clazz->descriptor, method->name);
2250 * Return the "this" object for the specified frame. The thread must be
2253 bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
2255 const u4* framePtr = frameIdToFrame(frameId);
2258 UNUSED_PARAMETER(threadId);
2260 thisObj = getThisObject(framePtr);
2262 *pThisId = objectToObjectId(thisObj);
2267 * Copy the value of a method argument or local variable into the
2268 * specified buffer. The value will be preceeded with the tag.
2270 * The debugger includes the tags in the request. Object tags may
2271 * be updated with a more refined type.
2273 void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
2274 u1 tag, u1* buf, int expectedLen)
2276 const u4* framePtr = frameIdToFrame(frameId);
2281 UNUSED_PARAMETER(threadId);
2283 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2287 assert(expectedLen == 1);
2288 intVal = framePtr[slot];
2289 set1(buf+1, intVal != 0);
2292 assert(expectedLen == 1);
2293 intVal = framePtr[slot];
2294 set1(buf+1, intVal);
2298 assert(expectedLen == 2);
2299 intVal = framePtr[slot];
2300 set2BE(buf+1, intVal);
2304 assert(expectedLen == 4);
2305 intVal = framePtr[slot];
2306 set4BE(buf+1, intVal);
2309 assert(expectedLen == sizeof(ObjectId));
2311 /* convert to "ObjectId" */
2312 objVal = (Object*)framePtr[slot];
2313 if (objVal != NULL && !dvmIsValidObject(objVal)) {
2314 LOGW("JDWP: slot %d expected to hold array, %p invalid\n",
2316 dvmAbort(); // DEBUG: make it obvious
2318 tag = JT_OBJECT; // JT_ARRAY not expected for NULL ref
2320 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2324 assert(expectedLen == sizeof(ObjectId));
2326 /* convert to "ObjectId" */
2327 objVal = (Object*)framePtr[slot];
2329 if (objVal != NULL && !dvmIsValidObject(objVal)) {
2330 LOGW("JDWP: slot %d expected to hold object, %p invalid\n",
2332 dvmAbort(); // DEBUG: make it obvious
2335 tag = tagFromObject(objVal);
2336 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2341 assert(expectedLen == 8);
2342 memcpy(&longVal, &framePtr[slot], 8);
2343 set8BE(buf+1, longVal);
2346 LOGE("ERROR: unhandled tag '%c'\n", tag);
2351 /* prepend tag, which may have been updated */
2356 * Copy a new value into an argument or local variable.
2358 void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
2359 u8 value, int width)
2361 u4* framePtr = frameIdToFrame(frameId);
2363 UNUSED_PARAMETER(threadId);
2365 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2370 framePtr[slot] = (u4)value;
2374 framePtr[slot] = (u4)value;
2379 framePtr[slot] = (u4)value;
2384 framePtr[slot] = (u4)value;
2387 /* The debugger calls VirtualMachine.CreateString to create a new
2388 * string, then uses this to set the object reference, when you
2389 * edit a String object */
2392 assert(width == sizeof(ObjectId));
2393 framePtr[slot] = (u4) objectIdToObject(value);
2398 memcpy(&framePtr[slot], &value, 8);
2401 case JT_CLASS_OBJECT:
2403 case JT_THREAD_GROUP:
2404 case JT_CLASS_LOADER:
2405 /* not expecting these from debugger; fall through to failure */
2407 LOGE("ERROR: unhandled tag '%c'\n", tag);
2415 * ===========================================================================
2416 * Debugger notification
2417 * ===========================================================================
2421 * Tell JDWP that a breakpoint address has been reached.
2423 * "pcOffset" will be -1 for native methods.
2424 * "thisPtr" will be NULL for static methods.
2426 void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
2427 Object* thisPtr, int eventFlags)
2431 if (dvmIsInterfaceClass(method->clazz))
2432 loc.typeTag = TT_INTERFACE;
2434 loc.typeTag = TT_CLASS;
2435 loc.classId = classObjectToRefTypeId(method->clazz);
2436 loc.methodId = methodToMethodId(method);
2440 * Note we use "NoReg" so we don't keep track of references that are
2441 * never actually sent to the debugger. The "thisPtr" is only used to
2442 * compare against registered events.
2445 if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
2446 objectToObjectIdNoReg(thisPtr), eventFlags))
2448 classObjectToRefTypeId(method->clazz);
2449 objectToObjectId(thisPtr);
2454 * Tell JDWP that an exception has occurred.
2456 void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
2457 int catchRelPc, Object* exception)
2459 JdwpLocation throwLoc, catchLoc;
2460 const Method* throwMeth;
2461 const Method* catchMeth;
2463 throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
2464 if (dvmIsInterfaceClass(throwMeth->clazz))
2465 throwLoc.typeTag = TT_INTERFACE;
2467 throwLoc.typeTag = TT_CLASS;
2468 throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
2469 throwLoc.methodId = methodToMethodId(throwMeth);
2470 throwLoc.idx = throwRelPc;
2472 if (catchRelPc < 0) {
2473 memset(&catchLoc, 0, sizeof(catchLoc));
2475 catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
2476 if (dvmIsInterfaceClass(catchMeth->clazz))
2477 catchLoc.typeTag = TT_INTERFACE;
2479 catchLoc.typeTag = TT_CLASS;
2480 catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
2481 catchLoc.methodId = methodToMethodId(catchMeth);
2482 catchLoc.idx = catchRelPc;
2485 /* need this for InstanceOnly filters */
2486 Object* thisObj = getThisObject((u4*)throwFp);
2489 * Hand the event to the JDWP exception handler. Note we're using the
2490 * "NoReg" objectID on the exception, which is not strictly correct --
2491 * the exception object WILL be passed up to the debugger if the
2492 * debugger is interested in the event. We do this because the current
2493 * implementation of the debugger object registry never throws anything
2494 * away, and some people were experiencing a fatal build up of exception
2495 * objects when dealing with certain libraries.
2497 dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
2498 objectToObjectIdNoReg(exception),
2499 classObjectToRefTypeId(exception->clazz), &catchLoc,
2500 objectToObjectId(thisObj));
2504 * Tell JDWP and/or DDMS that a thread has started.
2506 void dvmDbgPostThreadStart(Thread* thread)
2508 if (gDvm.debuggerActive) {
2509 dvmJdwpPostThreadChange(gDvm.jdwpState,
2510 objectToObjectId(thread->threadObj), true);
2512 if (gDvm.ddmThreadNotification)
2513 dvmDdmSendThreadNotification(thread, true);
2517 * Tell JDWP and/or DDMS that a thread has gone away.
2519 void dvmDbgPostThreadDeath(Thread* thread)
2521 if (gDvm.debuggerActive) {
2522 dvmJdwpPostThreadChange(gDvm.jdwpState,
2523 objectToObjectId(thread->threadObj), false);
2525 if (gDvm.ddmThreadNotification)
2526 dvmDdmSendThreadNotification(thread, false);
2530 * Tell JDWP that a new class has been prepared.
2532 void dvmDbgPostClassPrepare(ClassObject* clazz)
2534 const char* signature;
2537 if (dvmIsInterfaceClass(clazz))
2542 // TODO - we currently always send both "verified" and "prepared" since
2543 // debuggers seem to like that. There might be some advantage to honesty,
2544 // since the class may not yet be verified.
2545 signature = jniSignature(clazz);
2546 dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
2547 signature, CS_VERIFIED | CS_PREPARED);
2551 * The JDWP event mechanism has registered an event with a LocationOnly
2552 * mod. Tell the interpreter to call us if we hit the specified
2555 bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
2557 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2558 assert(!dvmIsNativeMethod(method));
2559 dvmAddBreakAddr(method, pLoc->idx);
2560 return true; /* assume success */
2564 * An event with a LocationOnly mod has been removed.
2566 void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
2568 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2569 assert(!dvmIsNativeMethod(method));
2570 dvmClearBreakAddr(method, pLoc->idx);
2574 * The JDWP event mechanism has registered a single-step event. Tell
2575 * the interpreter about it.
2577 bool dvmDbgConfigureStep(ObjectId threadId, enum JdwpStepSize size,
2578 enum JdwpStepDepth depth)
2582 bool result = false;
2584 threadObj = objectIdToObject(threadId);
2585 assert(threadObj != NULL);
2588 * Get a pointer to the Thread struct for this ID. The pointer will
2589 * be used strictly for comparisons against the current thread pointer
2590 * after the setup is complete, so we can safely release the lock.
2592 dvmLockThreadList(NULL);
2593 thread = threadObjToThread(threadObj);
2595 if (thread == NULL) {
2596 LOGE("Thread for single-step not found\n");
2599 if (!dvmIsSuspended(thread)) {
2600 LOGE("Thread for single-step not suspended\n");
2601 assert(!"non-susp step"); // I want to know if this can happen
2605 assert(dvmIsSuspended(thread));
2606 if (!dvmAddSingleStep(thread, size, depth))
2612 dvmUnlockThreadList();
2617 * A single-step event has been removed.
2619 void dvmDbgUnconfigureStep(ObjectId threadId)
2621 UNUSED_PARAMETER(threadId);
2623 /* right now it's global, so don't need to find Thread */
2624 dvmClearSingleStep(NULL);
2628 * Invoke a method in a thread that has been stopped on a breakpoint or
2629 * other debugger event. (This function is called from the JDWP thread.)
2631 * Note that access control is not enforced, per spec.
2633 JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
2634 RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
2635 u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
2637 Object* threadObj = objectIdToObject(threadId);
2639 dvmLockThreadList(NULL);
2641 Thread* targetThread = threadObjToThread(threadObj);
2642 if (targetThread == NULL) {
2643 dvmUnlockThreadList();
2644 return ERR_INVALID_THREAD; /* thread does not exist */
2646 if (!targetThread->invokeReq.ready) {
2647 dvmUnlockThreadList();
2648 return ERR_INVALID_THREAD; /* thread not stopped by event */
2652 * We currently have a bug where we don't successfully resume the
2653 * target thread if the suspend count is too deep. We're expected to
2654 * require one "resume" for each "suspend", but when asked to execute
2655 * a method we have to resume fully and then re-suspend it back to the
2656 * same level. (The easiest way to cause this is to type "suspend"
2657 * multiple times in jdb.)
2659 * It's unclear what this means when the event specifies "resume all"
2660 * and some threads are suspended more deeply than others. This is
2661 * a rare problem, so for now we just prevent it from hanging forever
2662 * by rejecting the method invocation request. Without this, we will
2663 * be stuck waiting on a suspended thread.
2665 if (targetThread->suspendCount > 1) {
2666 LOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
2667 "for method exec\n",
2668 dvmThreadSelf()->threadId, targetThread->threadId,
2669 targetThread->suspendCount);
2670 dvmUnlockThreadList();
2671 return ERR_THREAD_SUSPENDED; /* probably not expected here */
2675 * TODO: ought to screen the various IDs, and verify that the argument
2679 targetThread->invokeReq.obj = objectIdToObject(objectId);
2680 targetThread->invokeReq.thread = threadObj;
2681 targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
2682 targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
2683 targetThread->invokeReq.numArgs = numArgs;
2684 targetThread->invokeReq.argArray = argArray;
2685 targetThread->invokeReq.options = options;
2686 targetThread->invokeReq.invokeNeeded = true;
2689 * This is a bit risky -- if the thread goes away we're sitting high
2690 * and dry -- but we must release this before the dvmResumeAllThreads
2691 * call, and it's unwise to hold it during dvmWaitForSuspend.
2693 dvmUnlockThreadList();
2696 * We change our (JDWP thread) status, which should be THREAD_RUNNING,
2697 * so the VM can suspend for a GC if the invoke request causes us to
2698 * run out of memory. It's also a good idea to change it before locking
2699 * the invokeReq mutex, although that should never be held for long.
2701 Thread* self = dvmThreadSelf();
2702 ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
2704 LOGV(" Transferring control to event thread\n");
2705 dvmLockMutex(&targetThread->invokeReq.lock);
2707 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2708 LOGV(" Resuming all threads\n");
2709 dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2711 LOGV(" Resuming event thread only\n");
2712 dvmResumeThread(targetThread);
2716 * Wait for the request to finish executing.
2718 while (targetThread->invokeReq.invokeNeeded) {
2719 pthread_cond_wait(&targetThread->invokeReq.cv,
2720 &targetThread->invokeReq.lock);
2722 dvmUnlockMutex(&targetThread->invokeReq.lock);
2723 LOGV(" Control has returned from event thread\n");
2725 /* wait for thread to re-suspend itself */
2726 dvmWaitForSuspend(targetThread);
2729 * Done waiting, switch back to RUNNING.
2731 dvmChangeStatus(self, oldStatus);
2734 * Suspend the threads. We waited for the target thread to suspend
2735 * itself, so all we need to do is suspend the others.
2737 * The suspendAllThreads() call will double-suspend the event thread,
2738 * so we want to resume the target thread once to keep the books straight.
2740 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2741 LOGV(" Suspending all threads\n");
2742 dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2743 LOGV(" Resuming event thread to balance the count\n");
2744 dvmResumeThread(targetThread);
2748 * Set up the result.
2750 *pResultTag = targetThread->invokeReq.resultTag;
2751 if (isTagPrimitive(targetThread->invokeReq.resultTag))
2752 *pResultValue = targetThread->invokeReq.resultValue.j;
2754 Object* tmpObj = (Object*)targetThread->invokeReq.resultValue.l;
2755 *pResultValue = objectToObjectId(tmpObj);
2757 *pExceptObj = targetThread->invokeReq.exceptObj;
2758 return targetThread->invokeReq.err;
2762 * Return a basic tag value for the return type.
2764 static u1 getReturnTypeBasicTag(const Method* method)
2766 const char* descriptor = dexProtoGetReturnType(&method->prototype);
2767 return basicTagFromDescriptor(descriptor);
2771 * Execute the method described by "*pReq".
2773 * We're currently in VMWAIT, because we're stopped on a breakpoint. We
2774 * want to switch to RUNNING while we execute.
2776 void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
2778 Thread* self = dvmThreadSelf();
2781 ThreadStatus oldStatus;
2784 * We can be called while an exception is pending in the VM. We need
2785 * to preserve that across the method invocation.
2787 oldExcept = dvmGetException(self);
2788 if (oldExcept != NULL) {
2789 dvmAddTrackedAlloc(oldExcept, self);
2790 dvmClearException(self);
2793 oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
2796 * Translate the method through the vtable, unless we're calling a
2797 * direct method or the debugger wants to suppress it.
2799 if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
2800 dvmIsDirectMethod(pReq->method))
2802 meth = pReq->method;
2804 meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
2806 assert(meth != NULL);
2808 assert(sizeof(jvalue) == sizeof(u8));
2811 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2812 LOGV("JDWP invoking method %p/%p %s.%s:%s\n",
2813 pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
2817 dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
2818 (jvalue*)pReq->argArray);
2819 pReq->exceptObj = objectToObjectId(dvmGetException(self));
2820 pReq->resultTag = getReturnTypeBasicTag(meth);
2821 if (pReq->exceptObj != 0) {
2822 Object* exc = dvmGetException(self);
2823 LOGD(" JDWP invocation returning with exceptObj=%p (%s)\n",
2824 exc, exc->clazz->descriptor);
2825 //dvmLogExceptionStackTrace();
2826 dvmClearException(self);
2828 * Nothing should try to use this, but it looks like something is.
2829 * Make it null to be safe.
2831 pReq->resultValue.j = 0; /*0xadadadad;*/
2832 } else if (pReq->resultTag == JT_OBJECT) {
2833 /* if no exception thrown, examine object result more closely */
2834 u1 newTag = tagFromObject((Object*)pReq->resultValue.l);
2835 if (newTag != pReq->resultTag) {
2836 LOGVV(" JDWP promoted result from %d to %d\n",
2837 pReq->resultTag, newTag);
2838 pReq->resultTag = newTag;
2842 * Register the object. We don't actually need an ObjectId yet,
2843 * but we do need to be sure that the GC won't move or discard the
2844 * object when we switch out of RUNNING. The ObjectId conversion
2845 * will add the object to the "do not touch" list.
2847 * We can't use the "tracked allocation" mechanism here because
2848 * the object is going to be handed off to a different thread.
2850 objectToObjectId((Object*)pReq->resultValue.l);
2853 if (oldExcept != NULL) {
2854 dvmSetException(self, oldExcept);
2855 dvmReleaseTrackedAlloc(oldExcept, self);
2857 dvmChangeStatus(self, oldStatus);
2860 // for dvmAddressSetForLine
2861 typedef struct AddressSetContext {
2862 bool lastAddressValid;
2866 } AddressSetContext;
2868 // for dvmAddressSetForLine
2869 static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
2871 AddressSetContext *pContext = (AddressSetContext *)cnxt;
2873 if (lineNum == pContext->lineNum) {
2874 if (!pContext->lastAddressValid) {
2875 // Everything from this address until the next line change is ours
2876 pContext->lastAddress = address;
2877 pContext->lastAddressValid = true;
2879 // else, If we're already in a valid range for this lineNum,
2880 // just keep going (shouldn't really happen)
2881 } else if (pContext->lastAddressValid) { // and the line number is new
2883 // Add everything from the last entry up until here to the set
2884 for (i = pContext->lastAddress; i < address; i++) {
2885 dvmAddressSetSet(pContext->pSet, i);
2888 pContext->lastAddressValid = false;
2891 // there may be multiple entries for a line
2895 * Build up a set of bytecode addresses associated with a line number
2897 const AddressSet *dvmAddressSetForLine(const Method* method, int line)
2900 const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
2901 u4 insnsSize = dvmGetMethodInsnsSize(method);
2902 AddressSetContext context;
2904 result = (AddressSet*)calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
2905 result->setSize = insnsSize;
2907 memset(&context, 0, sizeof(context));
2908 context.pSet = result;
2909 context.lineNum = line;
2910 context.lastAddressValid = false;
2912 dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
2913 method->clazz->descriptor,
2914 method->prototype.protoIdx,
2915 method->accessFlags,
2916 addressSetCb, NULL, &context);
2918 // If the line number was the last in the position table...
2919 if (context.lastAddressValid) {
2921 for (i = context.lastAddress; i < insnsSize; i++) {
2922 dvmAddressSetSet(result, i);
2931 * ===========================================================================
2932 * Dalvik Debug Monitor support
2933 * ===========================================================================
2937 * We have received a DDM packet over JDWP. Hand it off to the VM.
2939 bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
2942 return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
2946 * First DDM packet has arrived over JDWP. Notify the press.
2948 void dvmDbgDdmConnected()
2954 * JDWP connection has dropped.
2956 void dvmDbgDdmDisconnected()
2958 dvmDdmDisconnected();
2962 * Send up a JDWP event packet with a DDM chunk in it.
2964 void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
2966 assert(buf != NULL);
2967 struct iovec vec[1] = { {(void*)buf, len} };
2968 dvmDbgDdmSendChunkV(type, vec, 1);
2972 * Send up a JDWP event packet with a DDM chunk in it. The chunk is
2973 * concatenated from multiple source buffers.
2975 void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
2977 if (gDvm.jdwpState == NULL) {
2978 LOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)\n",
2983 dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);