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 gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
101 return (gDvm.dbgRegistry != NULL);
105 * Free registry storage.
107 void dvmDebuggerShutdown(void)
109 dvmHashTableFree(gDvm.dbgRegistry);
110 gDvm.dbgRegistry = NULL;
115 * Pass these through to the VM functions. Allows extended checking
116 * (e.g. "errorcheck" mutexes). If nothing else we can assert() success.
118 void dvmDbgInitMutex(pthread_mutex_t* pMutex)
120 dvmInitMutex(pMutex);
122 void dvmDbgLockMutex(pthread_mutex_t* pMutex)
124 dvmLockMutex(pMutex);
126 void dvmDbgUnlockMutex(pthread_mutex_t* pMutex)
128 dvmUnlockMutex(pMutex);
130 void dvmDbgInitCond(pthread_cond_t* pCond)
132 pthread_cond_init(pCond, NULL);
134 void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
136 int cc = pthread_cond_wait(pCond, pMutex);
139 void dvmDbgCondSignal(pthread_cond_t* pCond)
141 int cc = pthread_cond_signal(pCond);
144 void dvmDbgCondBroadcast(pthread_cond_t* pCond)
146 int cc = pthread_cond_broadcast(pCond);
151 /* keep track of type, in case we need to distinguish them someday */
152 typedef enum RegistryType {
153 kObjectId = 0xc1, kRefTypeId
157 * Hash function for object IDs. Since objects are at least 8 bytes, and
158 * could someday be allocated on 16-byte boundaries, we don't want to use
159 * the low 4 bits in our hash.
161 static inline u4 registryHash(u4 val)
167 * (This is a dvmHashTableLookup() callback.)
169 static int registryCompare(const void* obj1, const void* obj2)
171 return (int) obj1 - (int) obj2;
176 * Determine if an id is already in the list.
178 * If the list doesn't yet exist, this creates it.
180 * Lock the registry before calling here.
182 static bool lookupId(ObjectId id)
186 found = dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
187 (void*)(u4) id, registryCompare, false);
190 assert(found == (void*)(u4) id);
195 * Register an object, if it hasn't already been.
197 * This is used for both ObjectId and RefTypeId. In theory we don't have
198 * to register RefTypeIds unless we're worried about classes unloading.
200 * Null references must be represented as zero, or the debugger will get
203 static ObjectId registerObject(const Object* obj, RegistryType type, bool reg)
210 assert((u4) obj != 0xcccccccc);
211 assert((u4) obj > 0x100);
213 id = (ObjectId)(u4)obj | ((u8) type) << 32;
217 dvmHashTableLock(gDvm.dbgRegistry);
218 if (!gDvm.debuggerConnected) {
219 /* debugger has detached while we were doing stuff? */
220 LOGI("ignoring registerObject request in thread=%d\n",
221 dvmThreadSelf()->threadId);
226 (void) dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
227 (void*)(u4) id, registryCompare, true);
230 dvmHashTableUnlock(gDvm.dbgRegistry);
235 * (This is a HashForeachFunc callback.)
237 static int markRef(void* data, void* arg)
239 UNUSED_PARAMETER(arg);
241 //LOGI("dbg mark %p\n", data);
242 dvmMarkObjectNonNull(data);
246 /* Mark all of the registered debugger references so the
247 * GC doesn't collect them.
249 void dvmGcMarkDebuggerRefs()
251 /* dvmDebuggerStartup() may not have been called before the first GC.
253 if (gDvm.dbgRegistry != NULL) {
254 dvmHashTableLock(gDvm.dbgRegistry);
255 dvmHashForeach(gDvm.dbgRegistry, markRef, NULL);
256 dvmHashTableUnlock(gDvm.dbgRegistry);
261 * Verify that an object has been registered. If it hasn't, the debugger
262 * is asking for something we didn't send it, which means something
263 * somewhere is broken.
265 * If speed is an issue we can encode the registry index in the high
266 * four bytes. We could also just hard-wire this to "true".
268 * Note this actually takes both ObjectId and RefTypeId.
270 static bool objectIsRegistered(ObjectId id, RegistryType type)
272 UNUSED_PARAMETER(type);
274 if (id == 0) // null reference?
277 dvmHashTableLock(gDvm.dbgRegistry);
278 bool result = lookupId(id);
279 dvmHashTableUnlock(gDvm.dbgRegistry);
284 * Convert to/from a RefTypeId.
286 * These are rarely NULL, but can be (e.g. java/lang/Object's superclass).
288 static RefTypeId classObjectToRefTypeId(ClassObject* clazz)
290 return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, true);
292 static RefTypeId classObjectToRefTypeIdNoReg(ClassObject* clazz)
294 return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, false);
296 static ClassObject* refTypeIdToClassObject(RefTypeId id)
298 assert(objectIsRegistered(id, kRefTypeId) || !gDvm.debuggerConnected);
299 return (ClassObject*)(u4) id;
303 * Convert to/from an ObjectId.
305 static ObjectId objectToObjectId(const Object* obj)
307 return registerObject(obj, kObjectId, true);
309 static ObjectId objectToObjectIdNoReg(const Object* obj)
311 return registerObject(obj, kObjectId, false);
313 static Object* objectIdToObject(ObjectId id)
315 assert(objectIsRegistered(id, kObjectId) || !gDvm.debuggerConnected);
316 return (Object*)(u4) id;
320 * Convert to/from a MethodId.
322 * These IDs are only guaranteed unique within a class, so they could be
323 * an enumeration index. For now we just use the Method*.
325 static MethodId methodToMethodId(const Method* meth)
327 return (MethodId)(u4) meth;
329 static Method* methodIdToMethod(RefTypeId refTypeId, MethodId id)
331 // TODO? verify "id" is actually a method in "refTypeId"
332 return (Method*)(u4) id;
336 * Convert to/from a FieldId.
338 * These IDs are only guaranteed unique within a class, so they could be
339 * an enumeration index. For now we just use the Field*.
341 static FieldId fieldToFieldId(const Field* field)
343 return (FieldId)(u4) field;
345 static Field* fieldIdToField(RefTypeId refTypeId, FieldId id)
347 // TODO? verify "id" is actually a field in "refTypeId"
348 return (Field*)(u4) id;
352 * Convert to/from a FrameId.
354 * We just return a pointer to the stack frame.
356 static FrameId frameToFrameId(const void* frame)
358 return (FrameId)(u4) frame;
360 static void* frameIdToFrame(FrameId id)
362 return (void*)(u4) id;
367 * Get the invocation request state.
369 DebugInvokeReq* dvmDbgGetInvokeReq(void)
371 return &dvmThreadSelf()->invokeReq;
375 * Enable the object registry, but don't enable debugging features yet.
377 * Only called from the JDWP handler thread.
379 void dvmDbgConnected(void)
381 assert(!gDvm.debuggerConnected);
383 LOGV("JDWP has attached\n");
384 assert(dvmHashTableNumEntries(gDvm.dbgRegistry) == 0);
385 gDvm.debuggerConnected = true;
389 * Enable all debugging features, including scans for breakpoints.
391 * This is a no-op if we're already active.
393 * Only called from the JDWP handler thread.
395 void dvmDbgActive(void)
397 if (gDvm.debuggerActive)
400 LOGI("Debugger is active\n");
401 dvmInitBreakpoints();
402 gDvm.debuggerActive = true;
406 * Disable debugging features.
408 * Set "debuggerConnected" to false, which disables use of the object
411 * Only called from the JDWP handler thread.
413 void dvmDbgDisconnected(void)
415 assert(gDvm.debuggerConnected);
417 gDvm.debuggerActive = false;
419 dvmHashTableLock(gDvm.dbgRegistry);
420 gDvm.debuggerConnected = false;
422 LOGI("Debugger has detached; object registry had %d entries\n",
423 dvmHashTableNumEntries(gDvm.dbgRegistry));
425 //for (i = 0; i < gDvm.dbgRegistryNext; i++)
426 // LOGVV("%4d: 0x%llx\n", i, gDvm.dbgRegistryTable[i]);
428 dvmHashTableClear(gDvm.dbgRegistry);
429 dvmHashTableUnlock(gDvm.dbgRegistry);
433 * Returns "true" if a debugger is connected.
435 * Does not return "true" if it's just a DDM server.
437 bool dvmDbgIsDebuggerConnected(void)
439 return gDvm.debuggerActive;
443 * Get time since last debugger activity. Used when figuring out if the
444 * debugger has finished configuring us.
446 s8 dvmDbgLastDebuggerActivity(void)
448 return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
452 * JDWP thread is running, don't allow GC.
454 int dvmDbgThreadRunning(void)
456 return dvmChangeStatus(NULL, THREAD_RUNNING);
460 * JDWP thread is idle, allow GC.
462 int dvmDbgThreadWaiting(void)
464 return dvmChangeStatus(NULL, THREAD_VMWAIT);
468 * Restore state returned by Running/Waiting calls.
470 int dvmDbgThreadContinuing(int status)
472 return dvmChangeStatus(NULL, status);
476 * The debugger wants us to exit.
478 void dvmDbgExit(int status)
480 // TODO? invoke System.exit() to perform exit processing; ends up
481 // in System.exitInternal(), which can call JNI exit hook
483 LOGI("GC lifetime allocation: %d bytes\n", gDvm.allocProf.allocCount);
484 if (CALC_CACHE_STATS) {
485 dvmDumpAtomicCacheStats(gDvm.instanceofCache);
486 dvmDumpBootClassPath();
489 #ifdef PROFILE_FIELD_ACCESS
490 dvmDumpFieldAccessCounts();
498 * ===========================================================================
499 * Class, Object, Array
500 * ===========================================================================
504 * Get the class's type descriptor from a reference type ID.
506 const char* dvmDbgGetClassDescriptor(RefTypeId id)
510 clazz = refTypeIdToClassObject(id);
511 return clazz->descriptor;
515 * Return the superclass of a class (will be NULL for java/lang/Object).
517 RefTypeId dvmDbgGetSuperclass(RefTypeId id)
519 ClassObject* clazz = refTypeIdToClassObject(id);
520 return classObjectToRefTypeId(clazz->super);
524 * Return a class's defining class loader.
526 RefTypeId dvmDbgGetClassLoader(RefTypeId id)
528 ClassObject* clazz = refTypeIdToClassObject(id);
529 return objectToObjectId(clazz->classLoader);
533 * Return a class's access flags.
535 u4 dvmDbgGetAccessFlags(RefTypeId id)
537 ClassObject* clazz = refTypeIdToClassObject(id);
538 return clazz->accessFlags & JAVA_FLAGS_MASK;
542 * Is this class an interface?
544 bool dvmDbgIsInterface(RefTypeId id)
546 ClassObject* clazz = refTypeIdToClassObject(id);
547 return dvmIsInterfaceClass(clazz);
551 * dvmHashForeach callback
553 static int copyRefType(void* vclazz, void* varg)
555 RefTypeId** pRefType = (RefTypeId**)varg;
556 **pRefType = classObjectToRefTypeId((ClassObject*) vclazz);
562 * Get the complete list of reference classes (i.e. all classes except
563 * the primitive types).
565 * Returns a newly-allocated buffer full of RefTypeId values.
567 void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf)
571 dvmHashTableLock(gDvm.loadedClasses);
572 *pNumClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
573 pRefType = *pClassRefBuf = malloc(sizeof(RefTypeId) * *pNumClasses);
575 if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
576 LOGW("Warning: problem getting class list\n");
577 /* not really expecting this to happen */
579 assert(pRefType - *pClassRefBuf == (int) *pNumClasses);
582 dvmHashTableUnlock(gDvm.loadedClasses);
586 * Get the list of reference classes "visible" to the specified class
587 * loader. A class is visible to a class loader if the ClassLoader object
588 * is the defining loader or is listed as an initiating loader.
590 * Returns a newly-allocated buffer full of RefTypeId values.
592 void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
593 RefTypeId** pClassRefBuf)
596 int numClasses = 0, maxClasses;
598 classLoader = objectIdToObject(classLoaderId);
599 // I don't think classLoader can be NULL, but the spec doesn't say
601 LOGVV("GetVisibleList: comparing to %p\n", classLoader);
603 dvmHashTableLock(gDvm.loadedClasses);
605 /* over-allocate the return buffer */
606 maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
607 *pClassRefBuf = malloc(sizeof(RefTypeId) * maxClasses);
610 * Run through the list, looking for matches.
613 for (dvmHashIterBegin(gDvm.loadedClasses, &iter); !dvmHashIterDone(&iter);
614 dvmHashIterNext(&iter))
616 ClassObject* clazz = (ClassObject*) dvmHashIterData(&iter);
618 if (clazz->classLoader == classLoader ||
619 dvmLoaderInInitiatingList(clazz, classLoader))
621 LOGVV(" match '%s'\n", clazz->descriptor);
622 (*pClassRefBuf)[numClasses++] = classObjectToRefTypeId(clazz);
625 *pNumClasses = numClasses;
627 dvmHashTableUnlock(gDvm.loadedClasses);
631 * Generate the "JNI signature" for a class, e.g. "Ljava/lang/String;".
633 * Our class descriptors are in the correct format, so we just copy that.
634 * TODO: figure out if we can avoid the copy now that we're using
635 * descriptors instead of unadorned class names.
637 * Returns a newly-allocated string.
639 static char* generateJNISignature(ClassObject* clazz)
641 return strdup(clazz->descriptor);
645 * Get information about a class.
647 * If "pSignature" is not NULL, *pSignature gets the "JNI signature" of
650 void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
653 ClassObject* clazz = refTypeIdToClassObject(classId);
655 if (clazz->descriptor[0] == '[') {
656 /* generated array class */
657 *pStatus = CS_VERIFIED | CS_PREPARED;
658 *pTypeTag = TT_ARRAY;
660 if (clazz->status == CLASS_ERROR)
663 *pStatus = CS_VERIFIED | CS_PREPARED | CS_INITIALIZED;
664 if (dvmIsInterfaceClass(clazz))
665 *pTypeTag = TT_INTERFACE;
667 *pTypeTag = TT_CLASS;
669 if (pSignature != NULL)
670 *pSignature = generateJNISignature(clazz);
674 * Search the list of loaded classes for a match.
676 bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
677 RefTypeId* pRefTypeId)
681 clazz = dvmFindLoadedClass(classDescriptor);
683 *pRefTypeId = classObjectToRefTypeId(clazz);
691 * Get an object's class and "type tag".
693 void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
694 RefTypeId* pRefTypeId)
696 Object* obj = objectIdToObject(objectId);
698 if (dvmIsArrayClass(obj->clazz))
699 *pRefTypeTag = TT_ARRAY;
700 else if (dvmIsInterfaceClass(obj->clazz))
701 *pRefTypeTag = TT_INTERFACE;
703 *pRefTypeTag = TT_CLASS;
704 *pRefTypeId = classObjectToRefTypeId(obj->clazz);
708 * Get a class object's "type tag".
710 u1 dvmDbgGetClassObjectType(RefTypeId refTypeId)
712 ClassObject* clazz = refTypeIdToClassObject(refTypeId);
714 if (dvmIsArrayClass(clazz))
716 else if (dvmIsInterfaceClass(clazz))
723 * Get a class' signature.
725 * Returns a newly-allocated string.
727 char* dvmDbgGetSignature(RefTypeId refTypeId)
731 clazz = refTypeIdToClassObject(refTypeId);
732 assert(clazz != NULL);
734 return generateJNISignature(clazz);
738 * Get class' source file.
740 * Returns a newly-allocated string.
742 const char* dvmDbgGetSourceFile(RefTypeId refTypeId)
746 clazz = refTypeIdToClassObject(refTypeId);
747 assert(clazz != NULL);
749 return clazz->sourceFile;
753 * Get an object's type name. Converted to a "JNI signature".
755 * Returns a newly-allocated string.
757 char* dvmDbgGetObjectTypeName(ObjectId objectId)
759 Object* obj = objectIdToObject(objectId);
763 return generateJNISignature(obj->clazz);
767 * Given a type signature (e.g. "Ljava/lang/String;"), return the JDWP
770 * In many cases this is necessary but not sufficient. For example, if
771 * we have a NULL String object, we want to return JT_STRING. If we have
772 * a java/lang/Object that holds a String reference, we also want to
773 * return JT_STRING. See dvmDbgGetObjectTag().
775 int dvmDbgGetSignatureTag(const char* type)
778 * We're not checking the class loader here (to guarantee that JT_STRING
779 * is truly the one and only String), but it probably doesn't matter
782 if (strcmp(type, "Ljava/lang/String;") == 0)
784 else if (strcmp(type, "Ljava/lang/Class;") == 0)
785 return JT_CLASS_OBJECT;
786 else if (strcmp(type, "Ljava/lang/Thread;") == 0)
788 else if (strcmp(type, "Ljava/lang/ThreadGroup;") == 0)
789 return JT_THREAD_GROUP;
790 else if (strcmp(type, "Ljava/lang/ClassLoader;") == 0)
791 return JT_CLASS_LOADER;
794 case '[': return JT_ARRAY;
795 case 'B': return JT_BYTE;
796 case 'C': return JT_CHAR;
797 case 'L': return JT_OBJECT;
798 case 'F': return JT_FLOAT;
799 case 'D': return JT_DOUBLE;
800 case 'I': return JT_INT;
801 case 'J': return JT_LONG;
802 case 'S': return JT_SHORT;
803 case 'V': return JT_VOID;
804 case 'Z': return JT_BOOLEAN;
806 LOGE("ERROR: unhandled type '%s'\n", type);
813 * Methods declared to return Object might actually be returning one
814 * of the "refined types". We need to check the object explicitly.
816 static u1 resultTagFromObject(Object* obj)
826 * Comparing against the known classes is faster than string
827 * comparisons. It ensures that we only find the classes in the
828 * bootstrap class loader, which may or may not be what we want.
830 if (clazz == gDvm.classJavaLangString)
832 else if (clazz == gDvm.classJavaLangClass)
833 return JT_CLASS_OBJECT;
834 else if (clazz == gDvm.classJavaLangThread)
836 else if (clazz == gDvm.classJavaLangThreadGroup)
837 return JT_THREAD_GROUP;
838 else if (strcmp(clazz->descriptor, "Ljava/lang/ClassLoader;") == 0)
839 return JT_CLASS_LOADER;
840 else if (clazz->descriptor[0] == '[')
847 * Determine the tag for an object with a known type.
849 int dvmDbgGetObjectTag(ObjectId objectId, const char* type)
853 tag = dvmDbgGetSignatureTag(type);
854 if (tag == JT_OBJECT && objectId != 0)
855 tag = resultTagFromObject(objectIdToObject(objectId));
861 * Get the widths of the specified JDWP.Tag value.
863 int dvmDbgGetTagWidth(int tag)
881 case JT_THREAD_GROUP:
882 case JT_CLASS_LOADER:
883 case JT_CLASS_OBJECT:
884 return sizeof(ObjectId);
889 LOGE("ERROR: unhandled tag '%c'\n", tag);
896 * Determine whether or not a tag represents a primitive type.
898 static bool isTagPrimitive(u1 tag)
914 case JT_CLASS_OBJECT:
916 case JT_THREAD_GROUP:
917 case JT_CLASS_LOADER:
920 LOGE("ERROR: unhandled tag '%c'\n", tag);
928 * Return the length of the specified array.
930 int dvmDbgGetArrayLength(ObjectId arrayId)
932 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
933 assert(dvmIsArray(arrayObj));
934 return arrayObj->length;
938 * Return a tag indicating the general type of elements in the array.
940 int dvmDbgGetArrayElementTag(ObjectId arrayId)
942 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
944 assert(dvmIsArray(arrayObj));
946 return dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
950 * Copy a series of values with the specified width, changing the byte
951 * ordering to big-endian.
953 static void copyValuesToBE(u1* out, const u1* in, int count, int width)
959 memcpy(out, in, count);
962 for (i = 0; i < count; i++)
963 *(((u2*) out)+i) = get2BE(in + i*2);
966 for (i = 0; i < count; i++)
967 *(((u4*) out)+i) = get4BE(in + i*4);
970 for (i = 0; i < count; i++)
971 *(((u8*) out)+i) = get8BE(in + i*8);
979 * Copy a series of values with the specified with, changing the
980 * byte order from big-endian.
982 static void copyValuesFromBE(u1* out, const u1* in, int count, int width)
988 memcpy(out, in, count);
991 for (i = 0; i < count; i++)
992 set2BE(out + i*2, *((u2*)in + i));
995 for (i = 0; i < count; i++)
996 set4BE(out + i*4, *((u4*)in + i));
999 for (i = 0; i < count; i++)
1000 set8BE(out + i*8, *((u8*)in + i));
1008 * Output a piece of an array to the reply buffer.
1010 * Returns "false" if something looks fishy.
1012 bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
1015 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
1016 const u1* data = (const u1*)arrayObj->contents;
1019 assert(dvmIsArray(arrayObj));
1021 if (firstIndex + count > (int)arrayObj->length) {
1022 LOGW("Request for index=%d + count=%d excceds length=%d\n",
1023 firstIndex, count, arrayObj->length);
1027 tag = dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
1029 if (isTagPrimitive(tag)) {
1030 int width = dvmDbgGetTagWidth(tag);
1033 outBuf = expandBufAddSpace(pReply, count * width);
1035 copyValuesToBE(outBuf, data + firstIndex*width, count, width);
1040 pObjects = (Object**) data;
1041 pObjects += firstIndex;
1043 LOGV(" --> copying %d object IDs\n", count);
1044 //assert(tag == JT_OBJECT); // could be object or "refined" type
1046 for (i = 0; i < count; i++, pObjects++) {
1048 if (*pObjects != NULL)
1049 thisTag = resultTagFromObject(*pObjects);
1052 expandBufAdd1(pReply, thisTag);
1053 expandBufAddObjectId(pReply, objectToObjectId(*pObjects));
1061 * Set a range of elements in an array from the data in "buf".
1063 bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
1066 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
1067 u1* data = (u1*)arrayObj->contents;
1070 assert(dvmIsArray(arrayObj));
1072 if (firstIndex + count > (int)arrayObj->length) {
1073 LOGW("Attempt to set index=%d + count=%d excceds length=%d\n",
1074 firstIndex, count, arrayObj->length);
1078 tag = dvmDbgGetSignatureTag(arrayObj->obj.clazz->descriptor + 1);
1080 if (isTagPrimitive(tag)) {
1081 int width = dvmDbgGetTagWidth(tag);
1083 LOGV(" --> setting %d '%c' width=%d\n", count, tag, width);
1085 copyValuesFromBE(data + firstIndex*width, buf, count, width);
1090 pObjects = (Object**) data;
1091 pObjects += firstIndex;
1093 LOGV(" --> setting %d objects", count);
1095 /* should do array type check here */
1096 for (i = 0; i < count; i++) {
1097 ObjectId id = dvmReadObjectId(&buf);
1098 *pObjects++ = objectIdToObject(id);
1106 * Create a new string.
1108 * The only place the reference will be held in the VM is in our registry.
1110 ObjectId dvmDbgCreateString(const char* str)
1112 StringObject* strObj;
1114 strObj = dvmCreateStringFromCstr(str, ALLOC_DEFAULT);
1115 dvmReleaseTrackedAlloc((Object*) strObj, NULL);
1116 return objectToObjectId((Object*) strObj);
1120 * Determine if "instClassId" is an instance of "classId".
1122 bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId)
1124 ClassObject* instClazz = refTypeIdToClassObject(instClassId);
1125 ClassObject* clazz = refTypeIdToClassObject(classId);
1127 return dvmInstanceof(instClazz, clazz);
1132 * ===========================================================================
1134 * ===========================================================================
1138 * Get the method name from a MethodId.
1140 const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id)
1144 meth = methodIdToMethod(refTypeId, id);
1149 * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
1150 * output all fields declared by the class. Inerhited fields are
1153 void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
1156 static const u1 genericSignature[1] = "";
1162 clazz = refTypeIdToClassObject(refTypeId);
1163 assert(clazz != NULL);
1165 declared = clazz->sfieldCount + clazz->ifieldCount;
1166 expandBufAdd4BE(pReply, declared);
1168 for (i = 0; i < clazz->sfieldCount; i++) {
1169 field = (Field*) &clazz->sfields[i];
1171 expandBufAddFieldId(pReply, fieldToFieldId(field));
1172 expandBufAddUtf8String(pReply, (const u1*) field->name);
1173 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1175 expandBufAddUtf8String(pReply, genericSignature);
1176 expandBufAdd4BE(pReply, field->accessFlags);
1178 for (i = 0; i < clazz->ifieldCount; i++) {
1179 field = (Field*) &clazz->ifields[i];
1181 expandBufAddFieldId(pReply, fieldToFieldId(field));
1182 expandBufAddUtf8String(pReply, (const u1*) field->name);
1183 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1185 expandBufAddUtf8String(pReply, genericSignature);
1186 expandBufAdd4BE(pReply, field->accessFlags);
1191 * For ReferenceType.Methods and ReferenceType.MethodsWithGeneric:
1192 * output all methods declared by the class. Inherited methods are
1195 void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
1198 DexStringCache stringCache;
1199 static const u1 genericSignature[1] = "";
1205 dexStringCacheInit(&stringCache);
1207 clazz = refTypeIdToClassObject(refTypeId);
1208 assert(clazz != NULL);
1210 declared = clazz->directMethodCount + clazz->virtualMethodCount;
1211 expandBufAdd4BE(pReply, declared);
1213 for (i = 0; i < clazz->directMethodCount; i++) {
1214 meth = &clazz->directMethods[i];
1216 expandBufAddMethodId(pReply, methodToMethodId(meth));
1217 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1219 expandBufAddUtf8String(pReply,
1220 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1224 expandBufAddUtf8String(pReply, genericSignature);
1225 expandBufAdd4BE(pReply, meth->accessFlags);
1227 for (i = 0; i < clazz->virtualMethodCount; i++) {
1228 meth = &clazz->virtualMethods[i];
1230 expandBufAddMethodId(pReply, methodToMethodId(meth));
1231 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1233 expandBufAddUtf8String(pReply,
1234 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1238 expandBufAddUtf8String(pReply, genericSignature);
1239 expandBufAdd4BE(pReply, meth->accessFlags);
1242 dexStringCacheRelease(&stringCache);
1246 * Output all interfaces directly implemented by the class.
1248 void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply)
1251 int i, start, count;
1253 clazz = refTypeIdToClassObject(refTypeId);
1254 assert(clazz != NULL);
1256 if (clazz->super == NULL)
1259 start = clazz->super->iftableCount;
1261 count = clazz->iftableCount - start;
1262 expandBufAdd4BE(pReply, count);
1263 for (i = start; i < clazz->iftableCount; i++) {
1264 ClassObject* iface = clazz->iftable[i].clazz;
1265 expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
1269 typedef struct DebugCallbackContext {
1272 // used by locals table
1274 } DebugCallbackContext;
1276 static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
1278 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1280 expandBufAdd8BE(pContext->pReply, address);
1281 expandBufAdd4BE(pContext->pReply, lineNum);
1282 pContext->numItems++;
1288 * For Method.LineTable: output the line table.
1290 * Note we operate in Dalvik's 16-bit units rather than bytes.
1292 void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
1298 DebugCallbackContext context;
1300 memset (&context, 0, sizeof(DebugCallbackContext));
1302 method = methodIdToMethod(refTypeId, methodId);
1303 if (dvmIsNativeMethod(method)) {
1308 end = dvmGetMethodInsnsSize(method);
1311 expandBufAdd8BE(pReply, start);
1312 expandBufAdd8BE(pReply, end);
1314 // Add numLines later
1315 size_t numLinesOffset = expandBufGetLength(pReply);
1316 expandBufAdd4BE(pReply, 0);
1318 context.pReply = pReply;
1320 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1321 dvmGetMethodCode(method),
1322 method->clazz->descriptor,
1323 method->prototype.protoIdx,
1324 method->accessFlags,
1325 lineTablePositionsCb, NULL, &context);
1327 set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
1331 * Eclipse appears to expect that the "this" reference is in slot zero.
1332 * If it's not, the "variables" display will show two copies of "this",
1333 * possibly because it gets "this" from SF.ThisObject and then displays
1334 * all locals with nonzero slot numbers.
1336 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
1337 * SF.GetValues / SF.SetValues we map them back.
1339 static int tweakSlot(int slot, const char* name)
1343 if (strcmp(name, "this") == 0) // only remap "this" ptr
1345 else if (slot == 0) // always remap slot 0
1346 newSlot = kSlot0Sub;
1348 LOGV("untweak: %d to %d\n", slot, newSlot);
1353 * Reverse Eclipse hack.
1355 static int untweakSlot(int slot, const void* framePtr)
1359 if (slot == kSlot0Sub) {
1361 } else if (slot == 0) {
1362 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
1363 const Method* method = saveArea->method;
1364 newSlot = method->registersSize - method->insSize;
1367 LOGV("untweak: %d to %d\n", slot, newSlot);
1371 static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
1372 u4 endAddress, const char *name, const char *descriptor,
1373 const char *signature)
1375 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1377 reg = (u2) tweakSlot(reg, name);
1379 LOGV(" %2d: %d(%d) '%s' '%s' slot=%d\n",
1380 pContext->numItems, startAddress, endAddress - startAddress,
1381 name, descriptor, reg);
1383 expandBufAdd8BE(pContext->pReply, startAddress);
1384 expandBufAddUtf8String(pContext->pReply, (const u1*)name);
1385 expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
1386 if (pContext->withGeneric) {
1387 expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
1389 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
1390 expandBufAdd4BE(pContext->pReply, reg);
1392 pContext->numItems++;
1396 * For Method.VariableTable[WithGeneric]: output information about local
1397 * variables for the specified method.
1399 void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
1400 bool withGeneric, ExpandBuf* pReply)
1403 DebugCallbackContext context;
1405 memset (&context, 0, sizeof(DebugCallbackContext));
1407 method = methodIdToMethod(refTypeId, methodId);
1409 expandBufAdd4BE(pReply, method->insSize);
1411 // Add numLocals later
1412 size_t numLocalsOffset = expandBufGetLength(pReply);
1413 expandBufAdd4BE(pReply, 0);
1415 context.pReply = pReply;
1416 context.withGeneric = withGeneric;
1417 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1418 dvmGetMethodCode(method),
1419 method->clazz->descriptor,
1420 method->prototype.protoIdx,
1421 method->accessFlags,
1422 NULL, variableTableCb, &context);
1424 set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
1428 * Get the type tag for the field's type.
1430 int dvmDbgGetFieldTag(ObjectId objId, FieldId fieldId)
1432 Object* obj = objectIdToObject(objId);
1433 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1434 Field* field = fieldIdToField(classId, fieldId);
1436 return dvmDbgGetSignatureTag(field->signature);
1440 * Get the type tag for the static field's type.
1442 int dvmDbgGetStaticFieldTag(RefTypeId refTypeId, FieldId fieldId)
1444 Field* field = fieldIdToField(refTypeId, fieldId);
1445 return dvmDbgGetSignatureTag(field->signature);
1449 * Copy the value of a field into the specified buffer.
1451 void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, u1* buf,
1454 Object* obj = objectIdToObject(objectId);
1455 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1456 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1461 switch (field->field.signature[0]) {
1463 assert(expectedLen == 1);
1464 intVal = dvmGetFieldBoolean(obj, field->byteOffset);
1465 set1(buf, intVal != 0);
1468 assert(expectedLen == 1);
1469 intVal = dvmGetFieldInt(obj, field->byteOffset);
1474 assert(expectedLen == 2);
1475 intVal = dvmGetFieldInt(obj, field->byteOffset);
1476 set2BE(buf, intVal);
1480 assert(expectedLen == 4);
1481 intVal = dvmGetFieldInt(obj, field->byteOffset);
1482 set4BE(buf, intVal);
1486 assert(expectedLen == sizeof(ObjectId));
1487 objVal = dvmGetFieldObject(obj, field->byteOffset);
1488 dvmSetObjectId(buf, objectToObjectId(objVal));
1492 assert(expectedLen == 8);
1493 longVal = dvmGetFieldLong(obj, field->byteOffset);
1494 set8BE(buf, longVal);
1497 LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
1504 * Set the value of the specified field.
1506 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
1509 Object* obj = objectIdToObject(objectId);
1510 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1511 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1513 switch (field->field.signature[0]) {
1516 dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
1520 dvmSetFieldInt(obj, field->byteOffset, value);
1525 dvmSetFieldInt(obj, field->byteOffset, value);
1530 dvmSetFieldInt(obj, field->byteOffset, value);
1534 assert(width == sizeof(ObjectId));
1535 dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
1540 dvmSetFieldLong(obj, field->byteOffset, value);
1543 LOGE("ERROR: unhandled class type '%s'\n", field->field.signature);
1550 * Copy the value of a static field into the specified buffer.
1552 void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId, u1* buf,
1555 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1559 switch (sfield->field.signature[0]) {
1561 assert(expectedLen == 1);
1562 set1(buf, dvmGetStaticFieldBoolean(sfield));
1565 assert(expectedLen == 1);
1566 set1(buf, dvmGetStaticFieldByte(sfield));
1569 assert(expectedLen == 2);
1570 set2BE(buf, dvmGetStaticFieldShort(sfield));
1573 assert(expectedLen == 2);
1574 set2BE(buf, dvmGetStaticFieldChar(sfield));
1577 assert(expectedLen == 4);
1578 set4BE(buf, dvmGetStaticFieldInt(sfield));
1581 assert(expectedLen == 4);
1582 value.f = dvmGetStaticFieldFloat(sfield);
1583 set4BE(buf, value.i);
1587 assert(expectedLen == sizeof(ObjectId));
1588 objVal = dvmGetStaticFieldObject(sfield);
1589 dvmSetObjectId(buf, objectToObjectId(objVal));
1592 assert(expectedLen == 8);
1593 set8BE(buf, dvmGetStaticFieldLong(sfield));
1596 assert(expectedLen == 8);
1597 value.d = dvmGetStaticFieldDouble(sfield);
1598 set8BE(buf, value.j);
1601 LOGE("ERROR: unhandled class type '%s'\n", sfield->field.signature);
1608 * Set the value of a static field.
1610 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1611 u8 rawValue, int width)
1613 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1619 switch (sfield->field.signature[0]) {
1622 dvmSetStaticFieldBoolean(sfield, value.z);
1626 dvmSetStaticFieldByte(sfield, value.b);
1630 dvmSetStaticFieldShort(sfield, value.s);
1634 dvmSetStaticFieldChar(sfield, value.c);
1638 dvmSetStaticFieldInt(sfield, value.i);
1642 dvmSetStaticFieldFloat(sfield, value.f);
1646 assert(width == sizeof(ObjectId));
1647 objVal = objectIdToObject(rawValue);
1648 dvmSetStaticFieldObject(sfield, objVal);
1652 dvmSetStaticFieldLong(sfield, value.j);
1656 dvmSetStaticFieldDouble(sfield, value.d);
1659 LOGE("ERROR: unhandled class type '%s'\n", sfield->field.signature);
1666 * Convert a string object to a UTF-8 string.
1668 * Returns a newly-allocated string.
1670 char* dvmDbgStringToUtf8(ObjectId strId)
1672 StringObject* strObj = (StringObject*) objectIdToObject(strId);
1674 return dvmCreateCstrFromString(strObj);
1679 * ===========================================================================
1680 * Thread and ThreadGroup
1681 * ===========================================================================
1685 * Convert a thread object to a Thread ptr.
1687 * This currently requires running through the list of threads and finding
1690 * IMPORTANT: grab gDvm.threadListLock before calling here.
1692 static Thread* threadObjToThread(Object* threadObj)
1696 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1697 if (thread->threadObj == threadObj)
1705 * Get the status and suspend state of a thread.
1707 bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
1712 bool result = false;
1714 threadObj = objectIdToObject(threadId);
1715 assert(threadObj != NULL);
1717 /* lock the thread list, so the thread doesn't vanish while we work */
1718 dvmLockThreadList(NULL);
1720 thread = threadObjToThread(threadObj);
1724 switch (thread->status) {
1725 case THREAD_ZOMBIE: *pThreadStatus = TS_ZOMBIE; break;
1726 case THREAD_RUNNING: *pThreadStatus = TS_RUNNING; break;
1727 case THREAD_TIMED_WAIT: *pThreadStatus = TS_SLEEPING; break;
1728 case THREAD_MONITOR: *pThreadStatus = TS_MONITOR; break;
1729 case THREAD_WAIT: *pThreadStatus = TS_WAIT; break;
1730 case THREAD_INITIALIZING: *pThreadStatus = TS_ZOMBIE; break;
1731 case THREAD_STARTING: *pThreadStatus = TS_ZOMBIE; break;
1732 case THREAD_NATIVE: *pThreadStatus = TS_RUNNING; break;
1733 case THREAD_VMWAIT: *pThreadStatus = TS_WAIT; break;
1736 *pThreadStatus = THREAD_ZOMBIE;
1740 if (dvmIsSuspended(thread))
1741 *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
1743 *pSuspendStatus = 0;
1748 dvmUnlockThreadList();
1753 * Get the thread's suspend count.
1755 u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
1761 threadObj = objectIdToObject(threadId);
1762 assert(threadObj != NULL);
1764 /* lock the thread list, so the thread doesn't vanish while we work */
1765 dvmLockThreadList(NULL);
1767 thread = threadObjToThread(threadObj);
1771 result = thread->suspendCount;
1774 dvmUnlockThreadList();
1779 * Determine whether or not a thread exists in the VM's thread list.
1781 * Returns "true" if the thread exists.
1783 bool dvmDbgThreadExists(ObjectId threadId)
1789 threadObj = objectIdToObject(threadId);
1790 assert(threadObj != NULL);
1792 /* lock the thread list, so the thread doesn't vanish while we work */
1793 dvmLockThreadList(NULL);
1795 thread = threadObjToThread(threadObj);
1801 dvmUnlockThreadList();
1806 * Determine whether or not a thread is suspended.
1808 * Returns "false" if the thread is running or doesn't exist.
1810 bool dvmDbgIsSuspended(ObjectId threadId)
1814 bool result = false;
1816 threadObj = objectIdToObject(threadId);
1817 assert(threadObj != NULL);
1819 /* lock the thread list, so the thread doesn't vanish while we work */
1820 dvmLockThreadList(NULL);
1822 thread = threadObjToThread(threadObj);
1826 result = dvmIsSuspended(thread);
1829 dvmUnlockThreadList();
1835 * Wait until a thread suspends.
1837 * We stray from the usual pattern here, and release the thread list lock
1838 * before we use the Thread. This is necessary and should be safe in this
1839 * circumstance; see comments in dvmWaitForSuspend().
1841 void dvmDbgWaitForSuspend(ObjectId threadId)
1846 threadObj = objectIdToObject(threadId);
1847 assert(threadObj != NULL);
1849 dvmLockThreadList(NULL);
1850 thread = threadObjToThread(threadObj);
1851 dvmUnlockThreadList();
1854 dvmWaitForSuspend(thread);
1860 * Return the ObjectId for the "system" thread group.
1862 ObjectId dvmDbgGetSystemThreadGroupId(void)
1864 Object* groupObj = dvmGetSystemThreadGroup();
1865 return objectToObjectId(groupObj);
1869 * Return the ObjectId for the "system" thread group.
1871 ObjectId dvmDbgGetMainThreadGroupId(void)
1873 Object* groupObj = dvmGetMainThreadGroup();
1874 return objectToObjectId(groupObj);
1878 * Get the name of a thread.
1880 * Returns a newly-allocated string.
1882 char* dvmDbgGetThreadName(ObjectId threadId)
1885 StringObject* nameStr;
1889 threadObj = objectIdToObject(threadId);
1890 assert(threadObj != NULL);
1892 nameStr = (StringObject*) dvmGetFieldObject(threadObj,
1893 gDvm.offJavaLangThread_name);
1894 str = dvmCreateCstrFromString(nameStr);
1895 result = (char*) malloc(strlen(str) + 20);
1897 /* lock the thread list, so the thread doesn't vanish while we work */
1898 dvmLockThreadList(NULL);
1899 Thread* thread = threadObjToThread(threadObj);
1901 sprintf(result, "<%d> %s", thread->threadId, str);
1903 sprintf(result, "%s", str);
1904 dvmUnlockThreadList();
1911 * Get a thread's group.
1913 ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
1918 threadObj = objectIdToObject(threadId);
1919 assert(threadObj != NULL);
1921 group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
1922 return objectToObjectId(group);
1927 * Get the name of a thread group.
1929 * Returns a newly-allocated string.
1931 char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
1933 Object* threadGroup;
1934 InstField* nameField;
1935 StringObject* nameStr;
1937 threadGroup = objectIdToObject(threadGroupId);
1938 assert(threadGroup != NULL);
1940 nameField = dvmFindInstanceField(gDvm.classJavaLangThreadGroup,
1941 "name", "Ljava/lang/String;");
1942 if (nameField == NULL) {
1943 LOGE("unable to find name field in ThreadGroup\n");
1947 nameStr = (StringObject*) dvmGetFieldObject(threadGroup,
1948 nameField->byteOffset);
1949 return dvmCreateCstrFromString(nameStr);
1953 * Get the parent of a thread group.
1955 * Returns a newly-allocated string.
1957 ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
1959 Object* threadGroup;
1960 InstField* parentField;
1963 threadGroup = objectIdToObject(threadGroupId);
1964 assert(threadGroup != NULL);
1966 parentField = dvmFindInstanceField(gDvm.classJavaLangThreadGroup,
1967 "parent", "Ljava/lang/ThreadGroup;");
1968 if (parentField == NULL) {
1969 LOGE("unable to find parent field in ThreadGroup\n");
1972 parent = dvmGetFieldObject(threadGroup, parentField->byteOffset);
1974 return objectToObjectId(parent);
1978 * Get the list of threads in the thread group.
1980 * We do this by running through the full list of threads and returning
1981 * the ones that have the ThreadGroup object as their owner.
1983 * If threadGroupId is set to "kAllThreads", we ignore the group field and
1984 * return all threads.
1986 * The caller must free "*ppThreadIds".
1988 void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
1989 ObjectId** ppThreadIds, u4* pThreadCount)
1991 Object* targetThreadGroup = NULL;
1992 InstField* groupField = NULL;
1996 if (threadGroupId != THREAD_GROUP_ALL) {
1997 targetThreadGroup = objectIdToObject(threadGroupId);
1998 assert(targetThreadGroup != NULL);
2001 groupField = dvmFindInstanceField(gDvm.classJavaLangThread,
2002 "group", "Ljava/lang/ThreadGroup;");
2004 dvmLockThreadList(NULL);
2006 thread = gDvm.threadList;
2008 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2011 /* Skip over the JDWP support thread. Some debuggers
2012 * get bent out of shape when they can't suspend and
2013 * query all threads, so it's easier if we just don't
2014 * tell them about us.
2016 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2019 /* This thread is currently being created, and isn't ready
2020 * to be seen by the debugger yet.
2022 if (thread->threadObj == NULL)
2025 group = dvmGetFieldObject(thread->threadObj, groupField->byteOffset);
2026 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2030 *pThreadCount = count;
2033 *ppThreadIds = NULL;
2036 ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
2038 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2041 /* Skip over the JDWP support thread. Some debuggers
2042 * get bent out of shape when they can't suspend and
2043 * query all threads, so it's easier if we just don't
2044 * tell them about us.
2046 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2049 /* This thread is currently being created, and isn't ready
2050 * to be seen by the debugger yet.
2052 if (thread->threadObj == NULL)
2055 group = dvmGetFieldObject(thread->threadObj,groupField->byteOffset);
2056 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2058 *ptr++ = objectToObjectId(thread->threadObj);
2066 dvmUnlockThreadList();
2072 * The caller must free "*ppThreadIds".
2074 void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
2076 dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
2081 * Count up the #of frames on the thread's stack.
2083 * Returns -1 on failure;
2085 int dvmDbgGetThreadFrameCount(ObjectId threadId)
2092 threadObj = objectIdToObject(threadId);
2094 dvmLockThreadList(NULL);
2096 thread = threadObjToThread(threadObj);
2100 framePtr = thread->curFrame;
2101 while (framePtr != NULL) {
2102 if (!dvmIsBreakFrame(framePtr))
2105 framePtr = SAVEAREA_FROM_FP(framePtr)->prevFrame;
2109 dvmUnlockThreadList();
2114 * Get info for frame N from the specified thread's stack.
2116 bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
2124 threadObj = objectIdToObject(threadId);
2126 dvmLockThreadList(NULL);
2128 thread = threadObjToThread(threadObj);
2132 framePtr = thread->curFrame;
2134 while (framePtr != NULL) {
2135 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2136 const Method* method = saveArea->method;
2138 if (!dvmIsBreakFrame(framePtr)) {
2140 *pFrameId = frameToFrameId(framePtr);
2141 if (dvmIsInterfaceClass(method->clazz))
2142 pLoc->typeTag = TT_INTERFACE;
2144 pLoc->typeTag = TT_CLASS;
2145 pLoc->classId = classObjectToRefTypeId(method->clazz);
2146 pLoc->methodId = methodToMethodId(method);
2147 if (dvmIsNativeMethod(method))
2150 pLoc->idx = saveArea->xtra.currentPc - method->insns;
2151 dvmUnlockThreadList();
2158 framePtr = saveArea->prevFrame;
2162 dvmUnlockThreadList();
2167 * Get the ThreadId for the current thread.
2169 ObjectId dvmDbgGetThreadSelfId(void)
2171 Thread* self = dvmThreadSelf();
2172 return objectToObjectId(self->threadObj);
2178 void dvmDbgSuspendVM(bool isEvent)
2180 dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
2186 void dvmDbgResumeVM()
2188 dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
2192 * Suspend one thread (not ourselves).
2194 void dvmDbgSuspendThread(ObjectId threadId)
2196 Object* threadObj = objectIdToObject(threadId);
2199 dvmLockThreadList(NULL);
2201 thread = threadObjToThread(threadObj);
2202 if (thread == NULL) {
2203 /* can happen if our ThreadDeath notify crosses in the mail */
2204 LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
2206 dvmSuspendThread(thread);
2209 dvmUnlockThreadList();
2213 * Resume one thread (not ourselves).
2215 void dvmDbgResumeThread(ObjectId threadId)
2217 Object* threadObj = objectIdToObject(threadId);
2220 dvmLockThreadList(NULL);
2222 thread = threadObjToThread(threadObj);
2223 if (thread == NULL) {
2224 LOGW("WARNING: threadid=%llx obj=%p no match\n", threadId, threadObj);
2226 dvmResumeThread(thread);
2229 dvmUnlockThreadList();
2233 * Suspend ourselves after sending an event to the debugger.
2235 void dvmDbgSuspendSelf(void)
2237 dvmSuspendSelf(true);
2241 * Get the "this" object for the specified frame.
2243 static Object* getThisObject(const u4* framePtr)
2245 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2246 const Method* method = saveArea->method;
2247 int argOffset = method->registersSize - method->insSize;
2250 if (method == NULL) {
2251 /* this is a "break" frame? */
2256 LOGVV(" Pulling this object for frame at %p\n", framePtr);
2257 LOGVV(" Method='%s' native=%d static=%d this=%p\n",
2258 method->name, dvmIsNativeMethod(method),
2259 dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
2262 * No "this" pointer for statics. No args on the interp stack for
2263 * native methods invoked directly from the VM.
2265 if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
2268 thisObj = (Object*) framePtr[argOffset];
2270 if (thisObj != NULL && !dvmIsValidObject(thisObj)) {
2271 LOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL\n",
2272 framePtr, method->clazz->descriptor, method->name);
2280 * Return the "this" object for the specified frame. The thread must be
2283 bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
2285 const u4* framePtr = frameIdToFrame(frameId);
2288 UNUSED_PARAMETER(threadId);
2290 thisObj = getThisObject(framePtr);
2292 *pThisId = objectToObjectId(thisObj);
2297 * Copy the value of a method argument or local variable into the
2298 * specified buffer. The value will be preceeded with the tag.
2300 void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
2301 u1 tag, u1* buf, int expectedLen)
2303 const u4* framePtr = frameIdToFrame(frameId);
2308 UNUSED_PARAMETER(threadId);
2310 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2314 assert(expectedLen == 1);
2315 intVal = framePtr[slot];
2316 set1(buf+1, intVal != 0);
2319 assert(expectedLen == 1);
2320 intVal = framePtr[slot];
2321 set1(buf+1, intVal);
2325 assert(expectedLen == 2);
2326 intVal = framePtr[slot];
2327 set2BE(buf+1, intVal);
2331 assert(expectedLen == 4);
2332 intVal = framePtr[slot];
2333 set4BE(buf+1, intVal);
2336 assert(expectedLen == 8);
2338 /* convert to "ObjectId" */
2339 objVal = (Object*)framePtr[slot];
2340 if (objVal != NULL && !dvmIsValidObject(objVal)) {
2341 LOGW("JDWP: slot %d expected to hold array, %p invalid\n",
2343 dvmAbort(); // DEBUG: make it obvious
2345 tag = JT_OBJECT; // JT_ARRAY not expected for NULL ref
2347 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2351 assert(expectedLen == 8);
2353 /* convert to "ObjectId" */
2354 objVal = (Object*)framePtr[slot];
2357 if (objVal != NULL) {
2358 if (!dvmIsValidObject(objVal)) {
2359 LOGW("JDWP: slot %d expected to hold object, %p invalid\n",
2361 dvmAbort(); // DEBUG: make it obvious
2364 //name = generateJNISignature(objVal->clazz);
2365 tag = resultTagFromObject(objVal);
2370 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2375 assert(expectedLen == 8);
2376 longVal = *(u8*)(&framePtr[slot]);
2377 set8BE(buf+1, longVal);
2380 LOGE("ERROR: unhandled tag '%c'\n", tag);
2389 * Copy a new value into an argument or local variable.
2391 void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
2392 u8 value, int width)
2394 u4* framePtr = frameIdToFrame(frameId);
2396 UNUSED_PARAMETER(threadId);
2398 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2403 framePtr[slot] = (u4)value;
2407 framePtr[slot] = (u4)value;
2412 framePtr[slot] = (u4)value;
2417 framePtr[slot] = (u4)value;
2420 /* The debugger calls VirtualMachine.CreateString to create a new
2421 * string, then uses this to set the object reference, when you
2422 * edit a String object */
2425 assert(width == sizeof(ObjectId));
2426 framePtr[slot] = (u4) objectIdToObject(value);
2431 *(u8*)(&framePtr[slot]) = value;
2434 case JT_CLASS_OBJECT:
2436 case JT_THREAD_GROUP:
2437 case JT_CLASS_LOADER:
2439 LOGE("ERROR: unhandled tag '%c'\n", tag);
2447 * ===========================================================================
2448 * Debugger notification
2449 * ===========================================================================
2453 * Tell JDWP that a breakpoint address has been reached.
2455 * "pcOffset" will be -1 for native methods.
2456 * "thisPtr" will be NULL for static methods.
2458 void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
2459 Object* thisPtr, int eventFlags)
2463 if (dvmIsInterfaceClass(method->clazz))
2464 loc.typeTag = TT_INTERFACE;
2466 loc.typeTag = TT_CLASS;
2467 loc.classId = classObjectToRefTypeId(method->clazz);
2468 loc.methodId = methodToMethodId(method);
2472 * Note we use "NoReg" so we don't keep track of references that are
2473 * never actually sent to the debugger. The "thisPtr" is used to
2474 * compare against registered events.
2477 if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
2478 objectToObjectIdNoReg(thisPtr), eventFlags))
2480 classObjectToRefTypeId(method->clazz);
2481 objectToObjectId(thisPtr);
2486 * Tell JDWP that an exception has occurred.
2488 void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
2489 int catchRelPc, Object* exception)
2491 JdwpLocation throwLoc, catchLoc;
2492 const Method* throwMeth;
2493 const Method* catchMeth;
2495 throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
2496 if (dvmIsInterfaceClass(throwMeth->clazz))
2497 throwLoc.typeTag = TT_INTERFACE;
2499 throwLoc.typeTag = TT_CLASS;
2500 throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
2501 throwLoc.methodId = methodToMethodId(throwMeth);
2502 throwLoc.idx = throwRelPc;
2504 if (catchRelPc < 0) {
2505 memset(&catchLoc, 0, sizeof(catchLoc));
2507 catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
2508 if (dvmIsInterfaceClass(catchMeth->clazz))
2509 catchLoc.typeTag = TT_INTERFACE;
2511 catchLoc.typeTag = TT_CLASS;
2512 catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
2513 catchLoc.methodId = methodToMethodId(catchMeth);
2514 catchLoc.idx = catchRelPc;
2517 /* need this for InstanceOnly filters */
2518 Object* thisObj = getThisObject(throwFp);
2520 dvmJdwpPostException(gDvm.jdwpState, &throwLoc, objectToObjectId(exception),
2521 classObjectToRefTypeId(exception->clazz), &catchLoc,
2522 objectToObjectId(thisObj));
2526 * Tell JDWP and/or DDMS that a thread has started.
2528 void dvmDbgPostThreadStart(Thread* thread)
2530 if (gDvm.debuggerActive) {
2531 dvmJdwpPostThreadChange(gDvm.jdwpState,
2532 objectToObjectId(thread->threadObj), true);
2534 if (gDvm.ddmThreadNotification)
2535 dvmDdmSendThreadNotification(thread, true);
2539 * Tell JDWP and/or DDMS that a thread has gone away.
2541 void dvmDbgPostThreadDeath(Thread* thread)
2543 if (gDvm.debuggerActive) {
2544 dvmJdwpPostThreadChange(gDvm.jdwpState,
2545 objectToObjectId(thread->threadObj), false);
2547 if (gDvm.ddmThreadNotification)
2548 dvmDdmSendThreadNotification(thread, false);
2552 * Tell JDWP that a new class has been prepared.
2554 void dvmDbgPostClassPrepare(ClassObject* clazz)
2559 if (dvmIsInterfaceClass(clazz))
2564 // TODO - we currently always send both "verified" and "prepared" since
2565 // debuggers seem to like that. There might be some advantage to honesty,
2566 // since the class may not yet be verified.
2567 signature = generateJNISignature(clazz);
2568 dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
2569 signature, CS_VERIFIED | CS_PREPARED);
2574 * The JDWP event mechanism has registered an event with a LocationOnly
2575 * mod. Tell the interpreter to call us if we hit the specified
2578 bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
2580 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2581 assert(!dvmIsNativeMethod(method));
2582 dvmAddBreakAddr(method, pLoc->idx);
2583 return true; /* assume success */
2587 * An event with a LocationOnly mod has been removed.
2589 void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
2591 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2592 assert(!dvmIsNativeMethod(method));
2593 dvmClearBreakAddr(method, pLoc->idx);
2597 * The JDWP event mechanism has registered a single-step event. Tell
2598 * the interpreter about it.
2600 bool dvmDbgConfigureStep(ObjectId threadId, enum JdwpStepSize size,
2601 enum JdwpStepDepth depth)
2605 bool result = false;
2607 threadObj = objectIdToObject(threadId);
2608 assert(threadObj != NULL);
2611 * Get a pointer to the Thread struct for this ID. The pointer will
2612 * be used strictly for comparisons against the current thread pointer
2613 * after the setup is complete, so we can safely release the lock.
2615 dvmLockThreadList(NULL);
2616 thread = threadObjToThread(threadObj);
2618 if (thread == NULL) {
2619 LOGE("Thread for single-step not found\n");
2622 if (!dvmIsSuspended(thread)) {
2623 LOGE("Thread for single-step not suspended\n");
2624 assert(!"non-susp step"); // I want to know if this can happen
2628 assert(dvmIsSuspended(thread));
2629 if (!dvmAddSingleStep(thread, size, depth))
2635 dvmUnlockThreadList();
2640 * A single-step event has been removed.
2642 void dvmDbgUnconfigureStep(ObjectId threadId)
2644 UNUSED_PARAMETER(threadId);
2646 /* right now it's global, so don't need to find Thread */
2647 dvmClearSingleStep(NULL);
2651 * Invoke a method in a thread that has been stopped on a breakpoint or
2652 * other debugger event. (This function is called from the JDWP thread.)
2654 * Note that access control is not enforced, per spec.
2656 JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
2657 RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
2658 u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
2660 Object* threadObj = objectIdToObject(threadId);
2661 Thread* targetThread;
2662 JdwpError err = ERR_NONE;
2664 dvmLockThreadList(NULL);
2666 targetThread = threadObjToThread(threadObj);
2667 if (targetThread == NULL) {
2668 err = ERR_INVALID_THREAD; /* thread does not exist */
2669 dvmUnlockThreadList();
2672 if (!targetThread->invokeReq.ready) {
2673 err = ERR_INVALID_THREAD; /* thread not stopped by event */
2674 dvmUnlockThreadList();
2679 * TODO: ought to screen the various IDs, and verify that the argument
2683 targetThread->invokeReq.obj = objectIdToObject(objectId);
2684 targetThread->invokeReq.thread = threadObj;
2685 targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
2686 targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
2687 targetThread->invokeReq.numArgs = numArgs;
2688 targetThread->invokeReq.argArray = argArray;
2689 targetThread->invokeReq.options = options;
2690 targetThread->invokeReq.invokeNeeded = true;
2693 * This is a bit risky -- if the thread goes away we're sitting high
2694 * and dry -- but we must release this before the dvmResumeAllThreads
2695 * call, and it's unwise to hold it during dvmWaitForSuspend.
2697 dvmUnlockThreadList();
2700 * We change our (JDWP thread) status, which should be THREAD_RUNNING,
2701 * so the VM can suspend for a GC if the invoke request causes us to
2702 * run out of memory. It's also a good idea to change it before locking
2703 * the invokeReq mutex, although that should never be held for long.
2705 Thread* self = dvmThreadSelf();
2706 int oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
2708 LOGV(" Transferring control to event thread\n");
2709 dvmLockMutex(&targetThread->invokeReq.lock);
2711 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2712 LOGV(" Resuming all threads\n");
2713 dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2715 LOGV(" Resuming event thread only\n");
2716 dvmResumeThread(targetThread);
2720 * Wait for the request to finish executing.
2722 while (targetThread->invokeReq.invokeNeeded) {
2723 pthread_cond_wait(&targetThread->invokeReq.cv,
2724 &targetThread->invokeReq.lock);
2726 dvmUnlockMutex(&targetThread->invokeReq.lock);
2727 LOGV(" Control has returned from event thread\n");
2729 /* wait for thread to re-suspend itself */
2730 dvmWaitForSuspend(targetThread);
2733 * Done waiting, switch back to RUNNING.
2735 dvmChangeStatus(self, oldStatus);
2738 * Suspend the threads. We waited for the target thread to suspend
2739 * itself, so all we need to do is suspend the others.
2741 * The suspendAllThreads() call will double-suspend the event thread,
2742 * so we want to resume the target thread once to keep the books straight.
2744 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2745 LOGV(" Suspending all threads\n");
2746 dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2747 LOGV(" Resuming event thread to balance the count\n");
2748 dvmResumeThread(targetThread);
2752 * Set up the result.
2754 *pResultTag = targetThread->invokeReq.resultTag;
2755 if (isTagPrimitive(targetThread->invokeReq.resultTag))
2756 *pResultValue = targetThread->invokeReq.resultValue.j;
2758 *pResultValue = objectToObjectId(targetThread->invokeReq.resultValue.l);
2759 *pExceptObj = targetThread->invokeReq.exceptObj;
2760 err = targetThread->invokeReq.err;
2767 * Determine the tag type for the return value for this method.
2769 static u1 resultTagFromSignature(const Method* method)
2771 const char* descriptor = dexProtoGetReturnType(&method->prototype);
2772 return dvmDbgGetSignatureTag(descriptor);
2776 * Execute the method described by "*pReq".
2778 * We're currently in VMWAIT, because we're stopped on a breakpoint. We
2779 * want to switch to RUNNING while we execute.
2781 void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
2783 Thread* self = dvmThreadSelf();
2789 * We can be called while an exception is pending in the VM. We need
2790 * to preserve that across the method invocation.
2792 oldExcept = dvmGetException(self);
2793 dvmClearException(self);
2795 oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
2798 * Translate the method through the vtable, unless we're calling a
2799 * static method or the debugger wants to suppress it.
2801 if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL) {
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 %s.%s %s\n",
2813 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 = resultTagFromSignature(meth);
2821 if (pReq->exceptObj != 0) {
2822 LOGD(" JDWP invocation returning with exceptObj=%p\n",
2823 dvmGetException(self));
2824 dvmClearException(self);
2826 * Nothing should try to use this, but it looks like something is.
2827 * Make it null to be safe.
2829 pReq->resultValue.j = 0; /*0xadadadad;*/
2830 } else if (pReq->resultTag == JT_OBJECT) {
2831 /* if no exception thrown, examine object result more closely */
2832 u1 newTag = resultTagFromObject(pReq->resultValue.l);
2833 if (newTag != pReq->resultTag) {
2834 LOGVV(" JDWP promoted result from %d to %d\n",
2835 pReq->resultTag, newTag);
2836 pReq->resultTag = newTag;
2840 if (oldExcept != NULL)
2841 dvmSetException(self, oldExcept);
2842 dvmChangeStatus(self, oldStatus);
2845 // for dvmAddressSetForLine
2846 typedef struct AddressSetContext {
2847 bool lastAddressValid;
2851 } AddressSetContext;
2853 // for dvmAddressSetForLine
2854 static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
2856 AddressSetContext *pContext = (AddressSetContext *)cnxt;
2858 if (lineNum == pContext->lineNum) {
2859 if (!pContext->lastAddressValid) {
2860 // Everything from this address until the next line change is ours
2861 pContext->lastAddress = address;
2862 pContext->lastAddressValid = true;
2864 // else, If we're already in a valid range for this lineNum,
2865 // just keep going (shouldn't really happen)
2866 } else if (pContext->lastAddressValid) { // and the line number is new
2868 // Add everything from the last entry up until here to the set
2869 for (i = pContext->lastAddress; i < address; i++) {
2870 dvmAddressSetSet(pContext->pSet, i);
2873 pContext->lastAddressValid = false;
2876 // there may be multiple entries for a line
2880 * Build up a set of bytecode addresses associated with a line number
2882 const AddressSet *dvmAddressSetForLine(const Method* method, int line)
2885 const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
2886 u4 insnsSize = dvmGetMethodInsnsSize(method);
2887 AddressSetContext context;
2889 result = calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
2890 result->setSize = insnsSize;
2892 memset(&context, 0, sizeof(context));
2893 context.pSet = result;
2894 context.lineNum = line;
2895 context.lastAddressValid = false;
2897 dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
2898 method->clazz->descriptor,
2899 method->prototype.protoIdx,
2900 method->accessFlags,
2901 addressSetCb, NULL, &context);
2903 // If the line number was the last in the position table...
2904 if (context.lastAddressValid) {
2906 for (i = context.lastAddress; i < insnsSize; i++) {
2907 dvmAddressSetSet(result, i);
2916 * ===========================================================================
2917 * Dalvik Debug Monitor support
2918 * ===========================================================================
2922 * We have received a DDM packet over JDWP. Hand it off to the VM.
2924 bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
2927 return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
2931 * First DDM packet has arrived over JDWP. Notify the press.
2933 void dvmDbgDdmConnected(void)
2939 * JDWP connection has dropped.
2941 void dvmDbgDdmDisconnected(void)
2943 dvmDdmDisconnected();
2947 * Send up a JDWP event packet with a DDM chunk in it.
2949 void dvmDbgDdmSendChunk(int type, int len, const u1* buf)
2951 if (gDvm.jdwpState == NULL) {
2952 LOGI("Debugger thread not active, ignoring DDM send (t=0x%08x l=%d)\n",
2957 dvmJdwpDdmSendChunk(gDvm.jdwpState, type, len, buf);