2 * Copyright (C) 2008 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Link between JDWP and the VM. The code here only runs as a result of
19 * requests from the debugger, so speed is not essential. Maintaining
20 * isolation of the JDWP code should make it easier to maintain and reuse.
22 * Collecting all debugger-related pieces here will also allow us to #ifdef
23 * the JDWP code out of release builds.
28 Notes on garbage collection and object registration
30 JDWP does not allow the debugger to assume that objects passed to it
31 will not be garbage collected. It specifies explicit commands (e.g.
32 ObjectReference.DisableCollection) to allow the debugger to manage
33 object lifetime. It does, however, require that the VM not re-use an
34 object ID unless an explicit "dispose" call has been made, and if the
35 VM asks for a now-collected object we must return INVALID_OBJECT.
37 JDWP also requires that, while the VM is suspended, no garbage collection
38 occur. The JDWP docs suggest that this is obvious, because no threads
39 can be running. Unfortunately it's not entirely clear how to deal
40 with situations where the debugger itself allocates strings or executes
41 code as part of displaying variables. The easiest way to enforce this,
42 short of disabling GC whenever the debugger is connected, is to ensure
43 that the debugger thread can't cause a GC: it has to expand the heap or
44 fail to allocate. (Might want to make that "is debugger thread AND all
45 other threads are suspended" to avoid unnecessary heap expansion by a
46 poorly-timed JDWP request.)
48 We use an "object registry" so that we can separate our internal
49 representation from what we show the debugger. This allows us to
50 return a registry table index instead of a pointer or handle.
52 There are various approaches we can take to achieve correct behavior:
54 (1) Disable garbage collection entirely while the debugger is attached.
55 This is very easy, but doesn't allow extended debugging sessions on
58 (2) Keep a list of all object references requested by or sent to the
59 debugger, and include the list in the GC root set. This ensures that
60 objects the debugger might care about don't go away. This is straightforward,
61 but it can cause us to hold on to large objects and prevent finalizers from
64 (3) Keep a list of what amount to weak object references. This way we
65 don't interfere with the GC, and can support JDWP requests like
66 "ObjectReference.IsCollected".
68 The current implementation is #2. The set should be reasonably small and
69 performance isn't critical, so a simple expanding array can be used.
74 The VM has a Thread struct associated with every active thread. The
75 ThreadId we pass to the debugger is the ObjectId for the java/lang/Thread
76 object, so to retrieve the VM's Thread struct we have to scan through the
77 list looking for a match.
79 When a thread goes away, we lock the list and free the struct. To
80 avoid having the thread list updated or Thread structs freed out from
81 under us, we want to acquire and hold the thread list lock while we're
82 performing operations on Threads. Exceptions to this rule are noted in
85 We can speed this up a bit by adding a Thread struct pointer to the
86 java/lang/Thread object, and ensuring that both are discarded at the
90 #define THREAD_GROUP_ALL ((ObjectId) 0x12345) // magic, internal-only value
92 #define kSlot0Sub 1000 // Eclipse workaround
95 * System init. We don't allocate the registry until first use.
96 * Make sure we do this before initializing JDWP.
98 bool dvmDebuggerStartup(void)
100 if (!dvmBreakpointStartup())
103 gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
104 return (gDvm.dbgRegistry != NULL);
108 * Free registry storage.
110 void dvmDebuggerShutdown(void)
112 dvmHashTableFree(gDvm.dbgRegistry);
113 gDvm.dbgRegistry = NULL;
114 dvmBreakpointShutdown();
119 * Pass these through to the VM functions. Allows extended checking
120 * (e.g. "errorcheck" mutexes). If nothing else we can assert() success.
122 void dvmDbgInitMutex(pthread_mutex_t* pMutex)
124 dvmInitMutex(pMutex);
126 void dvmDbgLockMutex(pthread_mutex_t* pMutex)
128 dvmLockMutex(pMutex);
130 void dvmDbgUnlockMutex(pthread_mutex_t* pMutex)
132 dvmUnlockMutex(pMutex);
134 void dvmDbgInitCond(pthread_cond_t* pCond)
136 pthread_cond_init(pCond, NULL);
138 void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
140 int cc __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 (void) 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 void* frameIdToFrame(FrameId id)
360 return (void*)(u4) id;
365 * Get the invocation request state.
367 DebugInvokeReq* dvmDbgGetInvokeReq(void)
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(void)
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.
393 void dvmDbgActive(void)
395 if (gDvm.debuggerActive)
398 LOGI("Debugger is active\n");
399 dvmInitBreakpoints();
400 gDvm.debuggerActive = true;
401 #if defined(WITH_JIT)
402 dvmCompilerStateRefresh();
407 * Disable debugging features.
409 * Set "debuggerConnected" to false, which disables use of the object
412 * Only called from the JDWP handler thread.
414 void dvmDbgDisconnected(void)
416 assert(gDvm.debuggerConnected);
418 gDvm.debuggerActive = false;
420 dvmHashTableLock(gDvm.dbgRegistry);
421 gDvm.debuggerConnected = false;
423 LOGD("Debugger has detached; object registry had %d entries\n",
424 dvmHashTableNumEntries(gDvm.dbgRegistry));
426 //for (i = 0; i < gDvm.dbgRegistryNext; i++)
427 // LOGVV("%4d: 0x%llx\n", i, gDvm.dbgRegistryTable[i]);
429 dvmHashTableClear(gDvm.dbgRegistry);
430 dvmHashTableUnlock(gDvm.dbgRegistry);
431 #if defined(WITH_JIT)
432 dvmCompilerStateRefresh();
437 * Returns "true" if a debugger is connected.
439 * Does not return "true" if it's just a DDM server.
441 bool dvmDbgIsDebuggerConnected(void)
443 return gDvm.debuggerActive;
447 * Get time since last debugger activity. Used when figuring out if the
448 * debugger has finished configuring us.
450 s8 dvmDbgLastDebuggerActivity(void)
452 return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
456 * JDWP thread is running, don't allow GC.
458 int dvmDbgThreadRunning(void)
460 return dvmChangeStatus(NULL, THREAD_RUNNING);
464 * JDWP thread is idle, allow GC.
466 int dvmDbgThreadWaiting(void)
468 return dvmChangeStatus(NULL, THREAD_VMWAIT);
472 * Restore state returned by Running/Waiting calls.
474 int dvmDbgThreadContinuing(int status)
476 return dvmChangeStatus(NULL, status);
480 * The debugger wants us to exit.
482 void dvmDbgExit(int status)
484 // TODO? invoke System.exit() to perform exit processing; ends up
485 // in System.exitInternal(), which can call JNI exit hook
486 LOGI("GC lifetime allocation: %d bytes\n", gDvm.allocProf.allocCount);
487 if (CALC_CACHE_STATS) {
488 dvmDumpAtomicCacheStats(gDvm.instanceofCache);
489 dvmDumpBootClassPath();
491 #ifdef PROFILE_FIELD_ACCESS
492 dvmDumpFieldAccessCounts();
500 * ===========================================================================
501 * Class, Object, Array
502 * ===========================================================================
506 * Get the class's type descriptor from a reference type ID.
508 const char* dvmDbgGetClassDescriptor(RefTypeId id)
512 clazz = refTypeIdToClassObject(id);
513 return clazz->descriptor;
517 * Convert a RefTypeId to an ObjectId.
519 ObjectId dvmDbgGetClassObject(RefTypeId id)
521 ClassObject* clazz = refTypeIdToClassObject(id);
522 return objectToObjectId((Object*) clazz);
526 * Return the superclass of a class (will be NULL for java/lang/Object).
528 RefTypeId dvmDbgGetSuperclass(RefTypeId id)
530 ClassObject* clazz = refTypeIdToClassObject(id);
531 return classObjectToRefTypeId(clazz->super);
535 * Return a class's defining class loader.
537 RefTypeId dvmDbgGetClassLoader(RefTypeId id)
539 ClassObject* clazz = refTypeIdToClassObject(id);
540 return objectToObjectId(clazz->classLoader);
544 * Return a class's access flags.
546 u4 dvmDbgGetAccessFlags(RefTypeId id)
548 ClassObject* clazz = refTypeIdToClassObject(id);
549 return clazz->accessFlags & JAVA_FLAGS_MASK;
553 * Is this class an interface?
555 bool dvmDbgIsInterface(RefTypeId id)
557 ClassObject* clazz = refTypeIdToClassObject(id);
558 return dvmIsInterfaceClass(clazz);
562 * dvmHashForeach callback
564 static int copyRefType(void* vclazz, void* varg)
566 RefTypeId** pRefType = (RefTypeId**)varg;
567 **pRefType = classObjectToRefTypeId((ClassObject*) vclazz);
573 * Get the complete list of reference classes (i.e. all classes except
574 * the primitive types).
576 * Returns a newly-allocated buffer full of RefTypeId values.
578 void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf)
582 dvmHashTableLock(gDvm.loadedClasses);
583 *pNumClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
584 pRefType = *pClassRefBuf = malloc(sizeof(RefTypeId) * *pNumClasses);
586 if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
587 LOGW("Warning: problem getting class list\n");
588 /* not really expecting this to happen */
590 assert(pRefType - *pClassRefBuf == (int) *pNumClasses);
593 dvmHashTableUnlock(gDvm.loadedClasses);
597 * Get the list of reference classes "visible" to the specified class
598 * loader. A class is visible to a class loader if the ClassLoader object
599 * is the defining loader or is listed as an initiating loader.
601 * Returns a newly-allocated buffer full of RefTypeId values.
603 void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
604 RefTypeId** pClassRefBuf)
607 int numClasses = 0, maxClasses;
609 classLoader = objectIdToObject(classLoaderId);
610 // I don't think classLoader can be NULL, but the spec doesn't say
612 LOGVV("GetVisibleList: comparing to %p\n", classLoader);
614 dvmHashTableLock(gDvm.loadedClasses);
616 /* over-allocate the return buffer */
617 maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
618 *pClassRefBuf = malloc(sizeof(RefTypeId) * maxClasses);
621 * Run through the list, looking for matches.
624 for (dvmHashIterBegin(gDvm.loadedClasses, &iter); !dvmHashIterDone(&iter);
625 dvmHashIterNext(&iter))
627 ClassObject* clazz = (ClassObject*) dvmHashIterData(&iter);
629 if (clazz->classLoader == classLoader ||
630 dvmLoaderInInitiatingList(clazz, classLoader))
632 LOGVV(" match '%s'\n", clazz->descriptor);
633 (*pClassRefBuf)[numClasses++] = classObjectToRefTypeId(clazz);
636 *pNumClasses = numClasses;
638 dvmHashTableUnlock(gDvm.loadedClasses);
642 * Generate the "JNI signature" for a class, e.g. "Ljava/lang/String;".
644 * Our class descriptors are in the correct format, so we just copy that.
645 * TODO: figure out if we can avoid the copy now that we're using
646 * descriptors instead of unadorned class names.
648 * Returns a newly-allocated string.
650 static char* generateJNISignature(ClassObject* clazz)
652 return strdup(clazz->descriptor);
656 * Get information about a class.
658 * If "pSignature" is not NULL, *pSignature gets the "JNI signature" of
661 void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
664 ClassObject* clazz = refTypeIdToClassObject(classId);
666 if (clazz->descriptor[0] == '[') {
667 /* generated array class */
668 *pStatus = CS_VERIFIED | CS_PREPARED;
669 *pTypeTag = TT_ARRAY;
671 if (clazz->status == CLASS_ERROR)
674 *pStatus = CS_VERIFIED | CS_PREPARED | CS_INITIALIZED;
675 if (dvmIsInterfaceClass(clazz))
676 *pTypeTag = TT_INTERFACE;
678 *pTypeTag = TT_CLASS;
680 if (pSignature != NULL)
681 *pSignature = generateJNISignature(clazz);
685 * Search the list of loaded classes for a match.
687 bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
688 RefTypeId* pRefTypeId)
692 clazz = dvmFindLoadedClass(classDescriptor);
694 *pRefTypeId = classObjectToRefTypeId(clazz);
702 * Get an object's class and "type tag".
704 void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
705 RefTypeId* pRefTypeId)
707 Object* obj = objectIdToObject(objectId);
709 if (dvmIsArrayClass(obj->clazz))
710 *pRefTypeTag = TT_ARRAY;
711 else if (dvmIsInterfaceClass(obj->clazz))
712 *pRefTypeTag = TT_INTERFACE;
714 *pRefTypeTag = TT_CLASS;
715 *pRefTypeId = classObjectToRefTypeId(obj->clazz);
719 * Get a class object's "type tag".
721 u1 dvmDbgGetClassObjectType(RefTypeId refTypeId)
723 ClassObject* clazz = refTypeIdToClassObject(refTypeId);
725 if (dvmIsArrayClass(clazz))
727 else if (dvmIsInterfaceClass(clazz))
734 * Get a class' signature.
736 * Returns a newly-allocated string.
738 char* dvmDbgGetSignature(RefTypeId refTypeId)
742 clazz = refTypeIdToClassObject(refTypeId);
743 assert(clazz != NULL);
745 return generateJNISignature(clazz);
749 * Get class' source file.
751 * Returns a newly-allocated string.
753 const char* dvmDbgGetSourceFile(RefTypeId refTypeId)
757 clazz = refTypeIdToClassObject(refTypeId);
758 assert(clazz != NULL);
760 return clazz->sourceFile;
764 * Get an object's type name. Converted to a "JNI signature".
766 * Returns a newly-allocated string.
768 char* dvmDbgGetObjectTypeName(ObjectId objectId)
770 Object* obj = objectIdToObject(objectId);
774 return generateJNISignature(obj->clazz);
778 * Given a type signature (e.g. "Ljava/lang/String;"), return the JDWP
781 * In many cases this is necessary but not sufficient. For example, if
782 * we have a NULL String object, we want to return JT_STRING. If we have
783 * a java/lang/Object that holds a String reference, we also want to
784 * return JT_STRING. See dvmDbgGetObjectTag().
786 int dvmDbgGetSignatureTag(const char* type)
789 * We're not checking the class loader here (to guarantee that JT_STRING
790 * is truly the one and only String), but it probably doesn't matter
793 if (strcmp(type, "Ljava/lang/String;") == 0)
795 else if (strcmp(type, "Ljava/lang/Class;") == 0)
796 return JT_CLASS_OBJECT;
797 else if (strcmp(type, "Ljava/lang/Thread;") == 0)
799 else if (strcmp(type, "Ljava/lang/ThreadGroup;") == 0)
800 return JT_THREAD_GROUP;
801 else if (strcmp(type, "Ljava/lang/ClassLoader;") == 0)
802 return JT_CLASS_LOADER;
805 case '[': return JT_ARRAY;
806 case 'B': return JT_BYTE;
807 case 'C': return JT_CHAR;
808 case 'L': return JT_OBJECT;
809 case 'F': return JT_FLOAT;
810 case 'D': return JT_DOUBLE;
811 case 'I': return JT_INT;
812 case 'J': return JT_LONG;
813 case 'S': return JT_SHORT;
814 case 'V': return JT_VOID;
815 case 'Z': return JT_BOOLEAN;
817 LOGE("ERROR: unhandled type '%s'\n", type);
824 * Methods declared to return Object might actually be returning one
825 * of the "refined types". We need to check the object explicitly.
827 static u1 resultTagFromObject(Object* obj)
837 * Comparing against the known classes is faster than string
838 * comparisons. It ensures that we only find the classes in the
839 * bootstrap class loader, which may or may not be what we want.
841 if (clazz == gDvm.classJavaLangString)
843 else if (clazz == gDvm.classJavaLangClass)
844 return JT_CLASS_OBJECT;
845 else if (clazz == gDvm.classJavaLangThread)
847 else if (clazz == gDvm.classJavaLangThreadGroup)
848 return JT_THREAD_GROUP;
849 else if (strcmp(clazz->descriptor, "Ljava/lang/ClassLoader;") == 0)
850 return JT_CLASS_LOADER;
851 else if (clazz->descriptor[0] == '[')
858 * Determine the tag for an object with a known type.
860 int dvmDbgGetObjectTag(ObjectId objectId, const char* type)
864 tag = dvmDbgGetSignatureTag(type);
865 if (tag == JT_OBJECT && objectId != 0)
866 tag = resultTagFromObject(objectIdToObject(objectId));
872 * Get the widths of the specified JDWP.Tag value.
874 int dvmDbgGetTagWidth(int tag)
892 case JT_THREAD_GROUP:
893 case JT_CLASS_LOADER:
894 case JT_CLASS_OBJECT:
895 return sizeof(ObjectId);
900 LOGE("ERROR: unhandled tag '%c'\n", tag);
907 * Determine whether or not a tag represents a primitive type.
909 static bool isTagPrimitive(u1 tag)
925 case JT_CLASS_OBJECT:
927 case JT_THREAD_GROUP:
928 case JT_CLASS_LOADER:
931 LOGE("ERROR: unhandled tag '%c'\n", tag);
939 * Return the length of the specified array.
941 int dvmDbgGetArrayLength(ObjectId arrayId)
943 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
944 assert(dvmIsArray(arrayObj));
945 return arrayObj->length;
949 * Return a tag indicating the general type of elements in the array.
951 int dvmDbgGetArrayElementTag(ObjectId arrayId)
953 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
955 assert(dvmIsArray(arrayObj));
957 return dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
961 * Copy a series of values with the specified width, changing the byte
962 * ordering to big-endian.
964 static void copyValuesToBE(u1* out, const u1* in, int count, int width)
970 memcpy(out, in, count);
973 for (i = 0; i < count; i++)
974 *(((u2*) out)+i) = get2BE(in + i*2);
977 for (i = 0; i < count; i++)
978 *(((u4*) out)+i) = get4BE(in + i*4);
981 for (i = 0; i < count; i++)
982 *(((u8*) out)+i) = get8BE(in + i*8);
990 * Copy a series of values with the specified with, changing the
991 * byte order from big-endian.
993 static void copyValuesFromBE(u1* out, const u1* in, int count, int width)
999 memcpy(out, in, count);
1002 for (i = 0; i < count; i++)
1003 set2BE(out + i*2, *((u2*)in + i));
1006 for (i = 0; i < count; i++)
1007 set4BE(out + i*4, *((u4*)in + i));
1010 for (i = 0; i < count; i++)
1011 set8BE(out + i*8, *((u8*)in + i));
1019 * Output a piece of an array to the reply buffer.
1021 * Returns "false" if something looks fishy.
1023 bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
1026 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
1027 const u1* data = (const u1*)arrayObj->contents;
1030 assert(dvmIsArray(arrayObj));
1032 if (firstIndex + count > (int)arrayObj->length) {
1033 LOGW("Request for index=%d + count=%d excceds length=%d\n",
1034 firstIndex, count, arrayObj->length);
1038 tag = dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
1040 if (isTagPrimitive(tag)) {
1041 int width = dvmDbgGetTagWidth(tag);
1044 outBuf = expandBufAddSpace(pReply, count * width);
1046 copyValuesToBE(outBuf, data + firstIndex*width, count, width);
1051 pObjects = (Object**) data;
1052 pObjects += firstIndex;
1054 LOGV(" --> copying %d object IDs\n", count);
1055 //assert(tag == JT_OBJECT); // could be object or "refined" type
1057 for (i = 0; i < count; i++, pObjects++) {
1059 if (*pObjects != NULL)
1060 thisTag = resultTagFromObject(*pObjects);
1063 expandBufAdd1(pReply, thisTag);
1064 expandBufAddObjectId(pReply, objectToObjectId(*pObjects));
1072 * Set a range of elements in an array from the data in "buf".
1074 bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
1077 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
1078 u1* data = (u1*)arrayObj->contents;
1081 assert(dvmIsArray(arrayObj));
1083 if (firstIndex + count > (int)arrayObj->length) {
1084 LOGW("Attempt to set index=%d + count=%d excceds length=%d\n",
1085 firstIndex, count, arrayObj->length);
1089 tag = dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
1091 if (isTagPrimitive(tag)) {
1092 int width = dvmDbgGetTagWidth(tag);
1094 LOGV(" --> setting %d '%c' width=%d\n", count, tag, width);
1096 copyValuesFromBE(data + firstIndex*width, buf, count, width);
1101 pObjects = (Object**) data;
1102 pObjects += firstIndex;
1104 LOGV(" --> setting %d objects", count);
1106 /* should do array type check here */
1107 for (i = 0; i < count; i++) {
1108 ObjectId id = dvmReadObjectId(&buf);
1109 *pObjects++ = objectIdToObject(id);
1117 * Create a new string.
1119 * The only place the reference will be held in the VM is in our registry.
1121 ObjectId dvmDbgCreateString(const char* str)
1123 StringObject* strObj;
1125 strObj = dvmCreateStringFromCstr(str);
1126 dvmReleaseTrackedAlloc((Object*) strObj, NULL);
1127 return objectToObjectId((Object*) strObj);
1131 * Allocate a new object of the specified type.
1133 * Add it to the registry to prevent it from being GCed.
1135 ObjectId dvmDbgCreateObject(RefTypeId classId)
1137 ClassObject* clazz = refTypeIdToClassObject(classId);
1138 Object* newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
1139 dvmReleaseTrackedAlloc(newObj, NULL);
1140 return objectToObjectId(newObj);
1144 * Allocate a new array object of the specified type and length. The
1145 * type is the array type, not the element type.
1147 * Add it to the registry to prevent it from being GCed.
1149 ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length)
1151 ClassObject* clazz = refTypeIdToClassObject(arrayTypeId);
1152 Object* newObj = (Object*) dvmAllocArrayByClass(clazz, length, ALLOC_DEFAULT);
1153 dvmReleaseTrackedAlloc(newObj, NULL);
1154 return objectToObjectId(newObj);
1158 * Determine if "instClassId" is an instance of "classId".
1160 bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId)
1162 ClassObject* instClazz = refTypeIdToClassObject(instClassId);
1163 ClassObject* clazz = refTypeIdToClassObject(classId);
1165 return dvmInstanceof(instClazz, clazz);
1170 * ===========================================================================
1172 * ===========================================================================
1176 * Get the method name from a MethodId.
1178 const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id)
1182 meth = methodIdToMethod(refTypeId, id);
1187 * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
1188 * output all fields declared by the class. Inerhited fields are
1191 void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
1194 static const u1 genericSignature[1] = "";
1200 clazz = refTypeIdToClassObject(refTypeId);
1201 assert(clazz != NULL);
1203 declared = clazz->sfieldCount + clazz->ifieldCount;
1204 expandBufAdd4BE(pReply, declared);
1206 for (i = 0; i < clazz->sfieldCount; i++) {
1207 field = (Field*) &clazz->sfields[i];
1209 expandBufAddFieldId(pReply, fieldToFieldId(field));
1210 expandBufAddUtf8String(pReply, (const u1*) field->name);
1211 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1213 expandBufAddUtf8String(pReply, genericSignature);
1214 expandBufAdd4BE(pReply, field->accessFlags);
1216 for (i = 0; i < clazz->ifieldCount; i++) {
1217 field = (Field*) &clazz->ifields[i];
1219 expandBufAddFieldId(pReply, fieldToFieldId(field));
1220 expandBufAddUtf8String(pReply, (const u1*) field->name);
1221 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1223 expandBufAddUtf8String(pReply, genericSignature);
1224 expandBufAdd4BE(pReply, field->accessFlags);
1229 * For ReferenceType.Methods and ReferenceType.MethodsWithGeneric:
1230 * output all methods declared by the class. Inherited methods are
1233 void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
1236 DexStringCache stringCache;
1237 static const u1 genericSignature[1] = "";
1243 dexStringCacheInit(&stringCache);
1245 clazz = refTypeIdToClassObject(refTypeId);
1246 assert(clazz != NULL);
1248 declared = clazz->directMethodCount + clazz->virtualMethodCount;
1249 expandBufAdd4BE(pReply, declared);
1251 for (i = 0; i < clazz->directMethodCount; i++) {
1252 meth = &clazz->directMethods[i];
1254 expandBufAddMethodId(pReply, methodToMethodId(meth));
1255 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1257 expandBufAddUtf8String(pReply,
1258 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1262 expandBufAddUtf8String(pReply, genericSignature);
1263 expandBufAdd4BE(pReply, meth->accessFlags);
1265 for (i = 0; i < clazz->virtualMethodCount; i++) {
1266 meth = &clazz->virtualMethods[i];
1268 expandBufAddMethodId(pReply, methodToMethodId(meth));
1269 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1271 expandBufAddUtf8String(pReply,
1272 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1276 expandBufAddUtf8String(pReply, genericSignature);
1277 expandBufAdd4BE(pReply, meth->accessFlags);
1280 dexStringCacheRelease(&stringCache);
1284 * Output all interfaces directly implemented by the class.
1286 void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply)
1289 int i, start, count;
1291 clazz = refTypeIdToClassObject(refTypeId);
1292 assert(clazz != NULL);
1294 if (clazz->super == NULL)
1297 start = clazz->super->iftableCount;
1299 count = clazz->iftableCount - start;
1300 expandBufAdd4BE(pReply, count);
1301 for (i = start; i < clazz->iftableCount; i++) {
1302 ClassObject* iface = clazz->iftable[i].clazz;
1303 expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
1307 typedef struct DebugCallbackContext {
1310 // used by locals table
1312 } DebugCallbackContext;
1314 static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
1316 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1318 expandBufAdd8BE(pContext->pReply, address);
1319 expandBufAdd4BE(pContext->pReply, lineNum);
1320 pContext->numItems++;
1326 * For Method.LineTable: output the line table.
1328 * Note we operate in Dalvik's 16-bit units rather than bytes.
1330 void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
1335 DebugCallbackContext context;
1337 memset (&context, 0, sizeof(DebugCallbackContext));
1339 method = methodIdToMethod(refTypeId, methodId);
1340 if (dvmIsNativeMethod(method)) {
1345 end = dvmGetMethodInsnsSize(method);
1348 expandBufAdd8BE(pReply, start);
1349 expandBufAdd8BE(pReply, end);
1351 // Add numLines later
1352 size_t numLinesOffset = expandBufGetLength(pReply);
1353 expandBufAdd4BE(pReply, 0);
1355 context.pReply = pReply;
1357 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1358 dvmGetMethodCode(method),
1359 method->clazz->descriptor,
1360 method->prototype.protoIdx,
1361 method->accessFlags,
1362 lineTablePositionsCb, NULL, &context);
1364 set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
1368 * Eclipse appears to expect that the "this" reference is in slot zero.
1369 * If it's not, the "variables" display will show two copies of "this",
1370 * possibly because it gets "this" from SF.ThisObject and then displays
1371 * all locals with nonzero slot numbers.
1373 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
1374 * SF.GetValues / SF.SetValues we map them back.
1376 static int tweakSlot(int slot, const char* name)
1380 if (strcmp(name, "this") == 0) // only remap "this" ptr
1382 else if (slot == 0) // always remap slot 0
1383 newSlot = kSlot0Sub;
1385 LOGV("untweak: %d to %d\n", slot, newSlot);
1390 * Reverse Eclipse hack.
1392 static int untweakSlot(int slot, const void* framePtr)
1396 if (slot == kSlot0Sub) {
1398 } else if (slot == 0) {
1399 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
1400 const Method* method = saveArea->method;
1401 newSlot = method->registersSize - method->insSize;
1404 LOGV("untweak: %d to %d\n", slot, newSlot);
1408 static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
1409 u4 endAddress, const char *name, const char *descriptor,
1410 const char *signature)
1412 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1414 reg = (u2) tweakSlot(reg, name);
1416 LOGV(" %2d: %d(%d) '%s' '%s' slot=%d\n",
1417 pContext->numItems, startAddress, endAddress - startAddress,
1418 name, descriptor, reg);
1420 expandBufAdd8BE(pContext->pReply, startAddress);
1421 expandBufAddUtf8String(pContext->pReply, (const u1*)name);
1422 expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
1423 if (pContext->withGeneric) {
1424 expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
1426 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
1427 expandBufAdd4BE(pContext->pReply, reg);
1429 pContext->numItems++;
1433 * For Method.VariableTable[WithGeneric]: output information about local
1434 * variables for the specified method.
1436 void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
1437 bool withGeneric, ExpandBuf* pReply)
1440 DebugCallbackContext context;
1442 memset (&context, 0, sizeof(DebugCallbackContext));
1444 method = methodIdToMethod(refTypeId, methodId);
1446 expandBufAdd4BE(pReply, method->insSize);
1448 // Add numLocals later
1449 size_t numLocalsOffset = expandBufGetLength(pReply);
1450 expandBufAdd4BE(pReply, 0);
1452 context.pReply = pReply;
1453 context.withGeneric = withGeneric;
1454 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1455 dvmGetMethodCode(method),
1456 method->clazz->descriptor,
1457 method->prototype.protoIdx,
1458 method->accessFlags,
1459 NULL, variableTableCb, &context);
1461 set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
1465 * Get the type tag for the field's type.
1467 int dvmDbgGetFieldTag(ObjectId objId, FieldId fieldId)
1469 Object* obj = objectIdToObject(objId);
1470 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1471 Field* field = fieldIdToField(classId, fieldId);
1473 return dvmDbgGetSignatureTag(field->signature);
1477 * Get the type tag for the static field's type.
1479 int dvmDbgGetStaticFieldTag(RefTypeId refTypeId, FieldId fieldId)
1481 Field* field = fieldIdToField(refTypeId, fieldId);
1482 return dvmDbgGetSignatureTag(field->signature);
1486 * Copy the value of a field into the specified buffer.
1488 void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, u1* buf,
1491 Object* obj = objectIdToObject(objectId);
1492 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1493 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1498 switch (field->field.signature[0]) {
1500 assert(expectedLen == 1);
1501 intVal = dvmGetFieldBoolean(obj, field->byteOffset);
1502 set1(buf, intVal != 0);
1505 assert(expectedLen == 1);
1506 intVal = dvmGetFieldInt(obj, field->byteOffset);
1511 assert(expectedLen == 2);
1512 intVal = dvmGetFieldInt(obj, field->byteOffset);
1513 set2BE(buf, intVal);
1517 assert(expectedLen == 4);
1518 intVal = dvmGetFieldInt(obj, field->byteOffset);
1519 set4BE(buf, intVal);
1523 assert(expectedLen == sizeof(ObjectId));
1524 objVal = dvmGetFieldObject(obj, field->byteOffset);
1525 dvmSetObjectId(buf, objectToObjectId(objVal));
1529 assert(expectedLen == 8);
1530 longVal = dvmGetFieldLong(obj, field->byteOffset);
1531 set8BE(buf, longVal);
1534 LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
1541 * Set the value of the specified field.
1543 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
1546 Object* obj = objectIdToObject(objectId);
1547 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1548 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1550 switch (field->field.signature[0]) {
1553 dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
1557 dvmSetFieldInt(obj, field->byteOffset, value);
1562 dvmSetFieldInt(obj, field->byteOffset, value);
1567 dvmSetFieldInt(obj, field->byteOffset, value);
1571 assert(width == sizeof(ObjectId));
1572 dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
1577 dvmSetFieldLong(obj, field->byteOffset, value);
1580 LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
1587 * Copy the value of a static field into the specified buffer.
1589 void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId, u1* buf,
1592 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1596 switch (sfield->field.signature[0]) {
1598 assert(expectedLen == 1);
1599 set1(buf, dvmGetStaticFieldBoolean(sfield));
1602 assert(expectedLen == 1);
1603 set1(buf, dvmGetStaticFieldByte(sfield));
1606 assert(expectedLen == 2);
1607 set2BE(buf, dvmGetStaticFieldShort(sfield));
1610 assert(expectedLen == 2);
1611 set2BE(buf, dvmGetStaticFieldChar(sfield));
1614 assert(expectedLen == 4);
1615 set4BE(buf, dvmGetStaticFieldInt(sfield));
1618 assert(expectedLen == 4);
1619 value.f = dvmGetStaticFieldFloat(sfield);
1620 set4BE(buf, value.i);
1624 assert(expectedLen == sizeof(ObjectId));
1625 objVal = dvmGetStaticFieldObject(sfield);
1626 dvmSetObjectId(buf, objectToObjectId(objVal));
1629 assert(expectedLen == 8);
1630 set8BE(buf, dvmGetStaticFieldLong(sfield));
1633 assert(expectedLen == 8);
1634 value.d = dvmGetStaticFieldDouble(sfield);
1635 set8BE(buf, value.j);
1638 LOGE("ERROR: unhandled class type '%s'\n", sfield->field.signature);
1645 * Set the value of a static field.
1647 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1648 u8 rawValue, int width)
1650 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1656 switch (sfield->field.signature[0]) {
1659 dvmSetStaticFieldBoolean(sfield, value.z);
1663 dvmSetStaticFieldByte(sfield, value.b);
1667 dvmSetStaticFieldShort(sfield, value.s);
1671 dvmSetStaticFieldChar(sfield, value.c);
1675 dvmSetStaticFieldInt(sfield, value.i);
1679 dvmSetStaticFieldFloat(sfield, value.f);
1683 assert(width == sizeof(ObjectId));
1684 objVal = objectIdToObject(rawValue);
1685 dvmSetStaticFieldObject(sfield, objVal);
1689 dvmSetStaticFieldLong(sfield, value.j);
1693 dvmSetStaticFieldDouble(sfield, value.d);
1696 LOGE("ERROR: unhandled class type '%s'\n", sfield->field.signature);
1703 * Convert a string object to a UTF-8 string.
1705 * Returns a newly-allocated string.
1707 char* dvmDbgStringToUtf8(ObjectId strId)
1709 StringObject* strObj = (StringObject*) objectIdToObject(strId);
1711 return dvmCreateCstrFromString(strObj);
1716 * ===========================================================================
1717 * Thread and ThreadGroup
1718 * ===========================================================================
1722 * Convert a thread object to a Thread ptr.
1724 * This currently requires running through the list of threads and finding
1727 * IMPORTANT: grab gDvm.threadListLock before calling here.
1729 static Thread* threadObjToThread(Object* threadObj)
1733 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1734 if (thread->threadObj == threadObj)
1742 * Get the status and suspend state of a thread.
1744 bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
1749 bool result = false;
1751 threadObj = objectIdToObject(threadId);
1752 assert(threadObj != NULL);
1754 /* lock the thread list, so the thread doesn't vanish while we work */
1755 dvmLockThreadList(NULL);
1757 thread = threadObjToThread(threadObj);
1761 switch (thread->status) {
1762 case THREAD_ZOMBIE: *pThreadStatus = TS_ZOMBIE; break;
1763 case THREAD_RUNNING: *pThreadStatus = TS_RUNNING; break;
1764 case THREAD_TIMED_WAIT: *pThreadStatus = TS_SLEEPING; break;
1765 case THREAD_MONITOR: *pThreadStatus = TS_MONITOR; break;
1766 case THREAD_WAIT: *pThreadStatus = TS_WAIT; break;
1767 case THREAD_INITIALIZING: *pThreadStatus = TS_ZOMBIE; break;
1768 case THREAD_STARTING: *pThreadStatus = TS_ZOMBIE; break;
1769 case THREAD_NATIVE: *pThreadStatus = TS_RUNNING; break;
1770 case THREAD_VMWAIT: *pThreadStatus = TS_WAIT; break;
1771 case THREAD_SUSPENDED: *pThreadStatus = TS_RUNNING; break;
1774 *pThreadStatus = THREAD_ZOMBIE;
1778 if (dvmIsSuspended(thread))
1779 *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
1781 *pSuspendStatus = 0;
1786 dvmUnlockThreadList();
1791 * Get the thread's suspend count.
1793 u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
1799 threadObj = objectIdToObject(threadId);
1800 assert(threadObj != NULL);
1802 /* lock the thread list, so the thread doesn't vanish while we work */
1803 dvmLockThreadList(NULL);
1805 thread = threadObjToThread(threadObj);
1809 result = thread->suspendCount;
1812 dvmUnlockThreadList();
1817 * Determine whether or not a thread exists in the VM's thread list.
1819 * Returns "true" if the thread exists.
1821 bool dvmDbgThreadExists(ObjectId threadId)
1827 threadObj = objectIdToObject(threadId);
1828 assert(threadObj != NULL);
1830 /* lock the thread list, so the thread doesn't vanish while we work */
1831 dvmLockThreadList(NULL);
1833 thread = threadObjToThread(threadObj);
1839 dvmUnlockThreadList();
1844 * Determine whether or not a thread is suspended.
1846 * Returns "false" if the thread is running or doesn't exist.
1848 bool dvmDbgIsSuspended(ObjectId threadId)
1852 bool result = false;
1854 threadObj = objectIdToObject(threadId);
1855 assert(threadObj != NULL);
1857 /* lock the thread list, so the thread doesn't vanish while we work */
1858 dvmLockThreadList(NULL);
1860 thread = threadObjToThread(threadObj);
1864 result = dvmIsSuspended(thread);
1867 dvmUnlockThreadList();
1873 * Wait until a thread suspends.
1875 * We stray from the usual pattern here, and release the thread list lock
1876 * before we use the Thread. This is necessary and should be safe in this
1877 * circumstance; see comments in dvmWaitForSuspend().
1879 void dvmDbgWaitForSuspend(ObjectId threadId)
1884 threadObj = objectIdToObject(threadId);
1885 assert(threadObj != NULL);
1887 dvmLockThreadList(NULL);
1888 thread = threadObjToThread(threadObj);
1889 dvmUnlockThreadList();
1892 dvmWaitForSuspend(thread);
1898 * Return the ObjectId for the "system" thread group.
1900 ObjectId dvmDbgGetSystemThreadGroupId(void)
1902 Object* groupObj = dvmGetSystemThreadGroup();
1903 return objectToObjectId(groupObj);
1907 * Return the ObjectId for the "system" thread group.
1909 ObjectId dvmDbgGetMainThreadGroupId(void)
1911 Object* groupObj = dvmGetMainThreadGroup();
1912 return objectToObjectId(groupObj);
1916 * Get the name of a thread.
1918 * Returns a newly-allocated string.
1920 char* dvmDbgGetThreadName(ObjectId threadId)
1923 StringObject* nameStr;
1927 threadObj = objectIdToObject(threadId);
1928 assert(threadObj != NULL);
1930 nameStr = (StringObject*) dvmGetFieldObject(threadObj,
1931 gDvm.offJavaLangThread_name);
1932 str = dvmCreateCstrFromString(nameStr);
1933 result = (char*) malloc(strlen(str) + 20);
1935 /* lock the thread list, so the thread doesn't vanish while we work */
1936 dvmLockThreadList(NULL);
1937 Thread* thread = threadObjToThread(threadObj);
1939 sprintf(result, "<%d> %s", thread->threadId, str);
1941 sprintf(result, "%s", str);
1942 dvmUnlockThreadList();
1949 * Get a thread's group.
1951 ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
1956 threadObj = objectIdToObject(threadId);
1957 assert(threadObj != NULL);
1959 group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
1960 return objectToObjectId(group);
1965 * Get the name of a thread group.
1967 * Returns a newly-allocated string.
1969 char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
1971 Object* threadGroup;
1972 InstField* nameField;
1973 StringObject* nameStr;
1975 threadGroup = objectIdToObject(threadGroupId);
1976 assert(threadGroup != NULL);
1978 nameField = dvmFindInstanceField(gDvm.classJavaLangThreadGroup,
1979 "name", "Ljava/lang/String;");
1980 if (nameField == NULL) {
1981 LOGE("unable to find name field in ThreadGroup\n");
1985 nameStr = (StringObject*) dvmGetFieldObject(threadGroup,
1986 nameField->byteOffset);
1987 return dvmCreateCstrFromString(nameStr);
1991 * Get the parent of a thread group.
1993 * Returns a newly-allocated string.
1995 ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
1997 Object* threadGroup;
1998 InstField* parentField;
2001 threadGroup = objectIdToObject(threadGroupId);
2002 assert(threadGroup != NULL);
2004 parentField = dvmFindInstanceField(gDvm.classJavaLangThreadGroup,
2005 "parent", "Ljava/lang/ThreadGroup;");
2006 if (parentField == NULL) {
2007 LOGE("unable to find parent field in ThreadGroup\n");
2010 parent = dvmGetFieldObject(threadGroup, parentField->byteOffset);
2012 return objectToObjectId(parent);
2016 * Get the list of threads in the thread group.
2018 * We do this by running through the full list of threads and returning
2019 * the ones that have the ThreadGroup object as their owner.
2021 * If threadGroupId is set to "kAllThreads", we ignore the group field and
2022 * return all threads.
2024 * The caller must free "*ppThreadIds".
2026 void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
2027 ObjectId** ppThreadIds, u4* pThreadCount)
2029 Object* targetThreadGroup = NULL;
2030 InstField* groupField = NULL;
2034 if (threadGroupId != THREAD_GROUP_ALL) {
2035 targetThreadGroup = objectIdToObject(threadGroupId);
2036 assert(targetThreadGroup != NULL);
2039 groupField = dvmFindInstanceField(gDvm.classJavaLangThread,
2040 "group", "Ljava/lang/ThreadGroup;");
2042 dvmLockThreadList(NULL);
2044 thread = gDvm.threadList;
2046 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2049 /* Skip over the JDWP support thread. Some debuggers
2050 * get bent out of shape when they can't suspend and
2051 * query all threads, so it's easier if we just don't
2052 * tell them about us.
2054 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2057 /* This thread is currently being created, and isn't ready
2058 * to be seen by the debugger yet.
2060 if (thread->threadObj == NULL)
2063 group = dvmGetFieldObject(thread->threadObj, groupField->byteOffset);
2064 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2068 *pThreadCount = count;
2071 *ppThreadIds = NULL;
2074 ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
2076 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2079 /* Skip over the JDWP support thread. Some debuggers
2080 * get bent out of shape when they can't suspend and
2081 * query all threads, so it's easier if we just don't
2082 * tell them about us.
2084 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2087 /* This thread is currently being created, and isn't ready
2088 * to be seen by the debugger yet.
2090 if (thread->threadObj == NULL)
2093 group = dvmGetFieldObject(thread->threadObj,groupField->byteOffset);
2094 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2096 *ptr++ = objectToObjectId(thread->threadObj);
2104 dvmUnlockThreadList();
2110 * The caller must free "*ppThreadIds".
2112 void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
2114 dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
2119 * Count up the #of frames on the thread's stack.
2121 * Returns -1 on failure;
2123 int dvmDbgGetThreadFrameCount(ObjectId threadId)
2130 threadObj = objectIdToObject(threadId);
2132 dvmLockThreadList(NULL);
2134 thread = threadObjToThread(threadObj);
2138 framePtr = thread->curFrame;
2139 while (framePtr != NULL) {
2140 if (!dvmIsBreakFrame(framePtr))
2143 framePtr = SAVEAREA_FROM_FP(framePtr)->prevFrame;
2147 dvmUnlockThreadList();
2152 * Get info for frame N from the specified thread's stack.
2154 bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
2162 threadObj = objectIdToObject(threadId);
2164 dvmLockThreadList(NULL);
2166 thread = threadObjToThread(threadObj);
2170 framePtr = thread->curFrame;
2172 while (framePtr != NULL) {
2173 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2174 const Method* method = saveArea->method;
2176 if (!dvmIsBreakFrame(framePtr)) {
2178 *pFrameId = frameToFrameId(framePtr);
2179 if (dvmIsInterfaceClass(method->clazz))
2180 pLoc->typeTag = TT_INTERFACE;
2182 pLoc->typeTag = TT_CLASS;
2183 pLoc->classId = classObjectToRefTypeId(method->clazz);
2184 pLoc->methodId = methodToMethodId(method);
2185 if (dvmIsNativeMethod(method))
2188 pLoc->idx = saveArea->xtra.currentPc - method->insns;
2189 dvmUnlockThreadList();
2196 framePtr = saveArea->prevFrame;
2200 dvmUnlockThreadList();
2205 * Get the ThreadId for the current thread.
2207 ObjectId dvmDbgGetThreadSelfId(void)
2209 Thread* self = dvmThreadSelf();
2210 return objectToObjectId(self->threadObj);
2216 void dvmDbgSuspendVM(bool isEvent)
2218 dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
2224 void dvmDbgResumeVM()
2226 dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
2230 * Suspend one thread (not ourselves).
2232 void dvmDbgSuspendThread(ObjectId threadId)
2234 Object* threadObj = objectIdToObject(threadId);
2237 dvmLockThreadList(NULL);
2239 thread = threadObjToThread(threadObj);
2240 if (thread == NULL) {
2241 /* can happen if our ThreadDeath notify crosses in the mail */
2242 LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
2244 dvmSuspendThread(thread);
2247 dvmUnlockThreadList();
2251 * Resume one thread (not ourselves).
2253 void dvmDbgResumeThread(ObjectId threadId)
2255 Object* threadObj = objectIdToObject(threadId);
2258 dvmLockThreadList(NULL);
2260 thread = threadObjToThread(threadObj);
2261 if (thread == NULL) {
2262 LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
2264 dvmResumeThread(thread);
2267 dvmUnlockThreadList();
2271 * Suspend ourselves after sending an event to the debugger.
2273 void dvmDbgSuspendSelf(void)
2275 dvmSuspendSelf(true);
2279 * Get the "this" object for the specified frame.
2281 static Object* getThisObject(const u4* framePtr)
2283 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2284 const Method* method = saveArea->method;
2285 int argOffset = method->registersSize - method->insSize;
2288 if (method == NULL) {
2289 /* this is a "break" frame? */
2294 LOGVV(" Pulling this object for frame at %p\n", framePtr);
2295 LOGVV(" Method='%s' native=%d static=%d this=%p\n",
2296 method->name, dvmIsNativeMethod(method),
2297 dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
2300 * No "this" pointer for statics. No args on the interp stack for
2301 * native methods invoked directly from the VM.
2303 if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
2306 thisObj = (Object*) framePtr[argOffset];
2308 if (thisObj != NULL && !dvmIsValidObject(thisObj)) {
2309 LOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL\n",
2310 framePtr, method->clazz->descriptor, method->name);
2318 * Return the "this" object for the specified frame. The thread must be
2321 bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
2323 const u4* framePtr = frameIdToFrame(frameId);
2326 UNUSED_PARAMETER(threadId);
2328 thisObj = getThisObject(framePtr);
2330 *pThisId = objectToObjectId(thisObj);
2335 * Copy the value of a method argument or local variable into the
2336 * specified buffer. The value will be preceeded with the tag.
2338 void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
2339 u1 tag, u1* buf, int expectedLen)
2341 const u4* framePtr = frameIdToFrame(frameId);
2346 UNUSED_PARAMETER(threadId);
2348 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2352 assert(expectedLen == 1);
2353 intVal = framePtr[slot];
2354 set1(buf+1, intVal != 0);
2357 assert(expectedLen == 1);
2358 intVal = framePtr[slot];
2359 set1(buf+1, intVal);
2363 assert(expectedLen == 2);
2364 intVal = framePtr[slot];
2365 set2BE(buf+1, intVal);
2369 assert(expectedLen == 4);
2370 intVal = framePtr[slot];
2371 set4BE(buf+1, intVal);
2374 assert(expectedLen == 8);
2376 /* convert to "ObjectId" */
2377 objVal = (Object*)framePtr[slot];
2378 if (objVal != NULL && !dvmIsValidObject(objVal)) {
2379 LOGW("JDWP: slot %d expected to hold array, %p invalid\n",
2381 dvmAbort(); // DEBUG: make it obvious
2383 tag = JT_OBJECT; // JT_ARRAY not expected for NULL ref
2385 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2389 assert(expectedLen == 8);
2391 /* convert to "ObjectId" */
2392 objVal = (Object*)framePtr[slot];
2395 if (objVal != NULL) {
2396 if (!dvmIsValidObject(objVal)) {
2397 LOGW("JDWP: slot %d expected to hold object, %p invalid\n",
2399 dvmAbort(); // DEBUG: make it obvious
2402 //name = generateJNISignature(objVal->clazz);
2403 tag = resultTagFromObject(objVal);
2408 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2413 assert(expectedLen == 8);
2414 longVal = *(u8*)(&framePtr[slot]);
2415 set8BE(buf+1, longVal);
2418 LOGE("ERROR: unhandled tag '%c'\n", tag);
2427 * Copy a new value into an argument or local variable.
2429 void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
2430 u8 value, int width)
2432 u4* framePtr = frameIdToFrame(frameId);
2434 UNUSED_PARAMETER(threadId);
2436 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2441 framePtr[slot] = (u4)value;
2445 framePtr[slot] = (u4)value;
2450 framePtr[slot] = (u4)value;
2455 framePtr[slot] = (u4)value;
2458 /* The debugger calls VirtualMachine.CreateString to create a new
2459 * string, then uses this to set the object reference, when you
2460 * edit a String object */
2463 assert(width == sizeof(ObjectId));
2464 framePtr[slot] = (u4) objectIdToObject(value);
2469 *(u8*)(&framePtr[slot]) = value;
2472 case JT_CLASS_OBJECT:
2474 case JT_THREAD_GROUP:
2475 case JT_CLASS_LOADER:
2477 LOGE("ERROR: unhandled tag '%c'\n", tag);
2485 * ===========================================================================
2486 * Debugger notification
2487 * ===========================================================================
2491 * Tell JDWP that a breakpoint address has been reached.
2493 * "pcOffset" will be -1 for native methods.
2494 * "thisPtr" will be NULL for static methods.
2496 void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
2497 Object* thisPtr, int eventFlags)
2501 if (dvmIsInterfaceClass(method->clazz))
2502 loc.typeTag = TT_INTERFACE;
2504 loc.typeTag = TT_CLASS;
2505 loc.classId = classObjectToRefTypeId(method->clazz);
2506 loc.methodId = methodToMethodId(method);
2510 * Note we use "NoReg" so we don't keep track of references that are
2511 * never actually sent to the debugger. The "thisPtr" is only used to
2512 * compare against registered events.
2515 if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
2516 objectToObjectIdNoReg(thisPtr), eventFlags))
2518 classObjectToRefTypeId(method->clazz);
2519 objectToObjectId(thisPtr);
2524 * Tell JDWP that an exception has occurred.
2526 void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
2527 int catchRelPc, Object* exception)
2529 JdwpLocation throwLoc, catchLoc;
2530 const Method* throwMeth;
2531 const Method* catchMeth;
2533 throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
2534 if (dvmIsInterfaceClass(throwMeth->clazz))
2535 throwLoc.typeTag = TT_INTERFACE;
2537 throwLoc.typeTag = TT_CLASS;
2538 throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
2539 throwLoc.methodId = methodToMethodId(throwMeth);
2540 throwLoc.idx = throwRelPc;
2542 if (catchRelPc < 0) {
2543 memset(&catchLoc, 0, sizeof(catchLoc));
2545 catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
2546 if (dvmIsInterfaceClass(catchMeth->clazz))
2547 catchLoc.typeTag = TT_INTERFACE;
2549 catchLoc.typeTag = TT_CLASS;
2550 catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
2551 catchLoc.methodId = methodToMethodId(catchMeth);
2552 catchLoc.idx = catchRelPc;
2555 /* need this for InstanceOnly filters */
2556 Object* thisObj = getThisObject(throwFp);
2559 * Hand the event to the JDWP exception handler. Note we're using the
2560 * "NoReg" objectID on the exception, which is not strictly correct --
2561 * the exception object WILL be passed up to the debugger if the
2562 * debugger is interested in the event. We do this because the current
2563 * implementation of the debugger object registry never throws anything
2564 * away, and some people were experiencing a fatal build up of exception
2565 * objects when dealing with certain libraries.
2567 dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
2568 objectToObjectIdNoReg(exception),
2569 classObjectToRefTypeId(exception->clazz), &catchLoc,
2570 objectToObjectId(thisObj));
2574 * Tell JDWP and/or DDMS that a thread has started.
2576 void dvmDbgPostThreadStart(Thread* thread)
2578 if (gDvm.debuggerActive) {
2579 dvmJdwpPostThreadChange(gDvm.jdwpState,
2580 objectToObjectId(thread->threadObj), true);
2582 if (gDvm.ddmThreadNotification)
2583 dvmDdmSendThreadNotification(thread, true);
2587 * Tell JDWP and/or DDMS that a thread has gone away.
2589 void dvmDbgPostThreadDeath(Thread* thread)
2591 if (gDvm.debuggerActive) {
2592 dvmJdwpPostThreadChange(gDvm.jdwpState,
2593 objectToObjectId(thread->threadObj), false);
2595 if (gDvm.ddmThreadNotification)
2596 dvmDdmSendThreadNotification(thread, false);
2600 * Tell JDWP that a new class has been prepared.
2602 void dvmDbgPostClassPrepare(ClassObject* clazz)
2607 if (dvmIsInterfaceClass(clazz))
2612 // TODO - we currently always send both "verified" and "prepared" since
2613 // debuggers seem to like that. There might be some advantage to honesty,
2614 // since the class may not yet be verified.
2615 signature = generateJNISignature(clazz);
2616 dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
2617 signature, CS_VERIFIED | CS_PREPARED);
2622 * The JDWP event mechanism has registered an event with a LocationOnly
2623 * mod. Tell the interpreter to call us if we hit the specified
2626 bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
2628 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2629 assert(!dvmIsNativeMethod(method));
2630 dvmAddBreakAddr(method, pLoc->idx);
2631 return true; /* assume success */
2635 * An event with a LocationOnly mod has been removed.
2637 void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
2639 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2640 assert(!dvmIsNativeMethod(method));
2641 dvmClearBreakAddr(method, pLoc->idx);
2645 * The JDWP event mechanism has registered a single-step event. Tell
2646 * the interpreter about it.
2648 bool dvmDbgConfigureStep(ObjectId threadId, enum JdwpStepSize size,
2649 enum JdwpStepDepth depth)
2653 bool result = false;
2655 threadObj = objectIdToObject(threadId);
2656 assert(threadObj != NULL);
2659 * Get a pointer to the Thread struct for this ID. The pointer will
2660 * be used strictly for comparisons against the current thread pointer
2661 * after the setup is complete, so we can safely release the lock.
2663 dvmLockThreadList(NULL);
2664 thread = threadObjToThread(threadObj);
2666 if (thread == NULL) {
2667 LOGE("Thread for single-step not found\n");
2670 if (!dvmIsSuspended(thread)) {
2671 LOGE("Thread for single-step not suspended\n");
2672 assert(!"non-susp step"); // I want to know if this can happen
2676 assert(dvmIsSuspended(thread));
2677 if (!dvmAddSingleStep(thread, size, depth))
2683 dvmUnlockThreadList();
2688 * A single-step event has been removed.
2690 void dvmDbgUnconfigureStep(ObjectId threadId)
2692 UNUSED_PARAMETER(threadId);
2694 /* right now it's global, so don't need to find Thread */
2695 dvmClearSingleStep(NULL);
2699 * Invoke a method in a thread that has been stopped on a breakpoint or
2700 * other debugger event. (This function is called from the JDWP thread.)
2702 * Note that access control is not enforced, per spec.
2704 JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
2705 RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
2706 u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
2708 Object* threadObj = objectIdToObject(threadId);
2709 Thread* targetThread;
2710 JdwpError err = ERR_NONE;
2712 dvmLockThreadList(NULL);
2714 targetThread = threadObjToThread(threadObj);
2715 if (targetThread == NULL) {
2716 err = ERR_INVALID_THREAD; /* thread does not exist */
2717 dvmUnlockThreadList();
2720 if (!targetThread->invokeReq.ready) {
2721 err = ERR_INVALID_THREAD; /* thread not stopped by event */
2722 dvmUnlockThreadList();
2727 * We currently have a bug where we don't successfully resume the
2728 * target thread if the suspend count is too deep. We're expected to
2729 * require one "resume" for each "suspend", but when asked to execute
2730 * a method we have to resume fully and then re-suspend it back to the
2731 * same level. (The easiest way to cause this is to type "suspend"
2732 * multiple times in jdb.)
2734 * It's unclear what this means when the event specifies "resume all"
2735 * and some threads are suspended more deeply than others. This is
2736 * a rare problem, so for now we just prevent it from hanging forever
2737 * by rejecting the method invocation request. Without this, we will
2738 * be stuck waiting on a suspended thread.
2740 if (targetThread->suspendCount > 1) {
2741 LOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
2742 "for method exec\n",
2743 dvmThreadSelf()->threadId, targetThread->threadId,
2744 targetThread->suspendCount);
2745 err = ERR_THREAD_SUSPENDED; /* probably not expected here */
2746 dvmUnlockThreadList();
2751 * TODO: ought to screen the various IDs, and verify that the argument
2755 targetThread->invokeReq.obj = objectIdToObject(objectId);
2756 targetThread->invokeReq.thread = threadObj;
2757 targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
2758 targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
2759 targetThread->invokeReq.numArgs = numArgs;
2760 targetThread->invokeReq.argArray = argArray;
2761 targetThread->invokeReq.options = options;
2762 targetThread->invokeReq.invokeNeeded = true;
2765 * This is a bit risky -- if the thread goes away we're sitting high
2766 * and dry -- but we must release this before the dvmResumeAllThreads
2767 * call, and it's unwise to hold it during dvmWaitForSuspend.
2769 dvmUnlockThreadList();
2772 * We change our (JDWP thread) status, which should be THREAD_RUNNING,
2773 * so the VM can suspend for a GC if the invoke request causes us to
2774 * run out of memory. It's also a good idea to change it before locking
2775 * the invokeReq mutex, although that should never be held for long.
2777 Thread* self = dvmThreadSelf();
2778 int oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
2780 LOGV(" Transferring control to event thread\n");
2781 dvmLockMutex(&targetThread->invokeReq.lock);
2783 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2784 LOGV(" Resuming all threads\n");
2785 dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2787 LOGV(" Resuming event thread only\n");
2788 dvmResumeThread(targetThread);
2792 * Wait for the request to finish executing.
2794 while (targetThread->invokeReq.invokeNeeded) {
2795 pthread_cond_wait(&targetThread->invokeReq.cv,
2796 &targetThread->invokeReq.lock);
2798 dvmUnlockMutex(&targetThread->invokeReq.lock);
2799 LOGV(" Control has returned from event thread\n");
2801 /* wait for thread to re-suspend itself */
2802 dvmWaitForSuspend(targetThread);
2805 * Done waiting, switch back to RUNNING.
2807 dvmChangeStatus(self, oldStatus);
2810 * Suspend the threads. We waited for the target thread to suspend
2811 * itself, so all we need to do is suspend the others.
2813 * The suspendAllThreads() call will double-suspend the event thread,
2814 * so we want to resume the target thread once to keep the books straight.
2816 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2817 LOGV(" Suspending all threads\n");
2818 dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2819 LOGV(" Resuming event thread to balance the count\n");
2820 dvmResumeThread(targetThread);
2824 * Set up the result.
2826 *pResultTag = targetThread->invokeReq.resultTag;
2827 if (isTagPrimitive(targetThread->invokeReq.resultTag))
2828 *pResultValue = targetThread->invokeReq.resultValue.j;
2830 *pResultValue = objectToObjectId(targetThread->invokeReq.resultValue.l);
2831 *pExceptObj = targetThread->invokeReq.exceptObj;
2832 err = targetThread->invokeReq.err;
2839 * Determine the tag type for the return value for this method.
2841 static u1 resultTagFromSignature(const Method* method)
2843 const char* descriptor = dexProtoGetReturnType(&method->prototype);
2844 return dvmDbgGetSignatureTag(descriptor);
2848 * Execute the method described by "*pReq".
2850 * We're currently in VMWAIT, because we're stopped on a breakpoint. We
2851 * want to switch to RUNNING while we execute.
2853 void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
2855 Thread* self = dvmThreadSelf();
2861 * We can be called while an exception is pending in the VM. We need
2862 * to preserve that across the method invocation.
2864 oldExcept = dvmGetException(self);
2865 if (oldExcept != NULL) {
2866 dvmAddTrackedAlloc(oldExcept, self);
2867 dvmClearException(self);
2870 oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
2873 * Translate the method through the vtable, unless we're calling a
2874 * direct method or the debugger wants to suppress it.
2876 if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
2877 dvmIsDirectMethod(pReq->method))
2879 meth = pReq->method;
2881 meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
2883 assert(meth != NULL);
2885 assert(sizeof(jvalue) == sizeof(u8));
2888 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2889 LOGV("JDWP invoking method %p/%p %s.%s:%s\n",
2890 pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
2894 dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
2895 (jvalue*)pReq->argArray);
2896 pReq->exceptObj = objectToObjectId(dvmGetException(self));
2897 pReq->resultTag = resultTagFromSignature(meth);
2898 if (pReq->exceptObj != 0) {
2899 Object* exc = dvmGetException(self);
2900 LOGD(" JDWP invocation returning with exceptObj=%p (%s)\n",
2901 exc, exc->clazz->descriptor);
2902 //dvmLogExceptionStackTrace();
2903 dvmClearException(self);
2905 * Nothing should try to use this, but it looks like something is.
2906 * Make it null to be safe.
2908 pReq->resultValue.j = 0; /*0xadadadad;*/
2909 } else if (pReq->resultTag == JT_OBJECT) {
2910 /* if no exception thrown, examine object result more closely */
2911 u1 newTag = resultTagFromObject(pReq->resultValue.l);
2912 if (newTag != pReq->resultTag) {
2913 LOGVV(" JDWP promoted result from %d to %d\n",
2914 pReq->resultTag, newTag);
2915 pReq->resultTag = newTag;
2919 * Register the object. We don't actually need an ObjectId yet,
2920 * but we do need to be sure that the GC won't move or discard the
2921 * object when we switch out of RUNNING. The ObjectId conversion
2922 * will add the object to the "do not touch" list.
2924 * We can't use the "tracked allocation" mechanism here because
2925 * the object is going to be handed off to a different thread.
2927 (void) objectToObjectId(pReq->resultValue.l);
2930 if (oldExcept != NULL) {
2931 dvmSetException(self, oldExcept);
2932 dvmReleaseTrackedAlloc(oldExcept, self);
2934 dvmChangeStatus(self, oldStatus);
2937 // for dvmAddressSetForLine
2938 typedef struct AddressSetContext {
2939 bool lastAddressValid;
2943 } AddressSetContext;
2945 // for dvmAddressSetForLine
2946 static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
2948 AddressSetContext *pContext = (AddressSetContext *)cnxt;
2950 if (lineNum == pContext->lineNum) {
2951 if (!pContext->lastAddressValid) {
2952 // Everything from this address until the next line change is ours
2953 pContext->lastAddress = address;
2954 pContext->lastAddressValid = true;
2956 // else, If we're already in a valid range for this lineNum,
2957 // just keep going (shouldn't really happen)
2958 } else if (pContext->lastAddressValid) { // and the line number is new
2960 // Add everything from the last entry up until here to the set
2961 for (i = pContext->lastAddress; i < address; i++) {
2962 dvmAddressSetSet(pContext->pSet, i);
2965 pContext->lastAddressValid = false;
2968 // there may be multiple entries for a line
2972 * Build up a set of bytecode addresses associated with a line number
2974 const AddressSet *dvmAddressSetForLine(const Method* method, int line)
2977 const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
2978 u4 insnsSize = dvmGetMethodInsnsSize(method);
2979 AddressSetContext context;
2981 result = calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
2982 result->setSize = insnsSize;
2984 memset(&context, 0, sizeof(context));
2985 context.pSet = result;
2986 context.lineNum = line;
2987 context.lastAddressValid = false;
2989 dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
2990 method->clazz->descriptor,
2991 method->prototype.protoIdx,
2992 method->accessFlags,
2993 addressSetCb, NULL, &context);
2995 // If the line number was the last in the position table...
2996 if (context.lastAddressValid) {
2998 for (i = context.lastAddress; i < insnsSize; i++) {
2999 dvmAddressSetSet(result, i);
3008 * ===========================================================================
3009 * Dalvik Debug Monitor support
3010 * ===========================================================================
3014 * We have received a DDM packet over JDWP. Hand it off to the VM.
3016 bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
3019 return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
3023 * First DDM packet has arrived over JDWP. Notify the press.
3025 void dvmDbgDdmConnected(void)
3031 * JDWP connection has dropped.
3033 void dvmDbgDdmDisconnected(void)
3035 dvmDdmDisconnected();
3039 * Send up a JDWP event packet with a DDM chunk in it.
3041 void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
3043 assert(buf != NULL);
3044 struct iovec vec[1] = { {(void*)buf, len} };
3045 dvmDbgDdmSendChunkV(type, vec, 1);
3049 * Send up a JDWP event packet with a DDM chunk in it. The chunk is
3050 * concatenated from multiple source buffers.
3052 void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
3054 if (gDvm.jdwpState == NULL) {
3055 LOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)\n",
3060 dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);