1 /*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17 /*
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.
21 *
22 * Collecting all debugger-related pieces here will also allow us to #ifdef
23 * the JDWP code out of release builds.
24 */
25 #include "Dalvik.h"
26
27 /*
28 Notes on garbage collection and object registration
29
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.
36
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.)
47
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.
51
52 There are various approaches we can take to achieve correct behavior:
53
54 (1) Disable garbage collection entirely while the debugger is attached.
55 This is very easy, but doesn't allow extended debugging sessions on
56 small devices.
57
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
62 being executed.
63
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".
67
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.
70
71
72 Notes on threads:
73
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.
78
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
83 a couple of places.
84
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
87 same time.
88 */
89
90 #define THREAD_GROUP_ALL ((ObjectId) 0x12345) // magic, internal-only value
91
92 #define kSlot0Sub 1000 // Eclipse workaround
93
94 /*
95 * System init. We don't allocate the registry until first use.
96 * Make sure we do this before initializing JDWP.
97 */
dvmDebuggerStartup()98 bool dvmDebuggerStartup()
99 {
100 if (!dvmBreakpointStartup())
101 return false;
102
103 gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
104 return (gDvm.dbgRegistry != NULL);
105 }
106
107 /*
108 * Free registry storage.
109 */
dvmDebuggerShutdown()110 void dvmDebuggerShutdown()
111 {
112 dvmHashTableFree(gDvm.dbgRegistry);
113 gDvm.dbgRegistry = NULL;
114 dvmBreakpointShutdown();
115 }
116
117
118 /*
119 * Pass these through to the VM functions. Allows extended checking
120 * (e.g. "errorcheck" mutexes). If nothing else we can assert() success.
121 */
dvmDbgInitMutex(pthread_mutex_t * pMutex)122 void dvmDbgInitMutex(pthread_mutex_t* pMutex)
123 {
124 dvmInitMutex(pMutex);
125 }
dvmDbgLockMutex(pthread_mutex_t * pMutex)126 void dvmDbgLockMutex(pthread_mutex_t* pMutex)
127 {
128 dvmLockMutex(pMutex);
129 }
dvmDbgUnlockMutex(pthread_mutex_t * pMutex)130 void dvmDbgUnlockMutex(pthread_mutex_t* pMutex)
131 {
132 dvmUnlockMutex(pMutex);
133 }
dvmDbgInitCond(pthread_cond_t * pCond)134 void dvmDbgInitCond(pthread_cond_t* pCond)
135 {
136 pthread_cond_init(pCond, NULL);
137 }
dvmDbgCondWait(pthread_cond_t * pCond,pthread_mutex_t * pMutex)138 void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
139 {
140 int cc __attribute__ ((__unused__)) = pthread_cond_wait(pCond, pMutex);
141 assert(cc == 0);
142 }
dvmDbgCondSignal(pthread_cond_t * pCond)143 void dvmDbgCondSignal(pthread_cond_t* pCond)
144 {
145 int cc __attribute__ ((__unused__)) = pthread_cond_signal(pCond);
146 assert(cc == 0);
147 }
dvmDbgCondBroadcast(pthread_cond_t * pCond)148 void dvmDbgCondBroadcast(pthread_cond_t* pCond)
149 {
150 int cc __attribute__ ((__unused__)) = pthread_cond_broadcast(pCond);
151 assert(cc == 0);
152 }
153
154
155 /* keep track of type, in case we need to distinguish them someday */
156 enum RegistryType {
157 kObjectId = 0xc1, kRefTypeId
158 };
159
160 /*
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.
164 */
registryHash(u4 val)165 static inline u4 registryHash(u4 val)
166 {
167 return val >> 4;
168 }
169
170 /*
171 * (This is a dvmHashTableLookup() callback.)
172 */
registryCompare(const void * obj1,const void * obj2)173 static int registryCompare(const void* obj1, const void* obj2)
174 {
175 return (int) obj1 - (int) obj2;
176 }
177
178
179 /*
180 * Determine if an id is already in the list.
181 *
182 * If the list doesn't yet exist, this creates it.
183 *
184 * Lock the registry before calling here.
185 */
186 #ifndef NDEBUG
lookupId(ObjectId id)187 static bool lookupId(ObjectId id)
188 {
189 void* found;
190
191 found = dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
192 (void*)(u4) id, registryCompare, false);
193 if (found == NULL)
194 return false;
195 assert(found == (void*)(u4) id);
196 return true;
197 }
198 #endif
199
200 /*
201 * Register an object, if it hasn't already been.
202 *
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.
205 *
206 * Null references must be represented as zero, or the debugger will get
207 * very confused.
208 */
registerObject(const Object * obj,RegistryType type,bool reg)209 static ObjectId registerObject(const Object* obj, RegistryType type, bool reg)
210 {
211 ObjectId id;
212
213 if (obj == NULL)
214 return 0;
215
216 assert((u4) obj != 0xcccccccc);
217 assert((u4) obj > 0x100);
218
219 id = (ObjectId)(u4)obj | ((u8) type) << 32;
220 if (!reg)
221 return id;
222
223 dvmHashTableLock(gDvm.dbgRegistry);
224 if (!gDvm.debuggerConnected) {
225 /* debugger has detached while we were doing stuff? */
226 ALOGI("ignoring registerObject request in thread=%d",
227 dvmThreadSelf()->threadId);
228 //dvmAbort();
229 goto bail;
230 }
231
232 dvmHashTableLookup(gDvm.dbgRegistry, registryHash((u4) id),
233 (void*)(u4) id, registryCompare, true);
234
235 bail:
236 dvmHashTableUnlock(gDvm.dbgRegistry);
237 return id;
238 }
239
240 /*
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.
244 *
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".
247 *
248 * Note this actually takes both ObjectId and RefTypeId.
249 */
250 #ifndef NDEBUG
objectIsRegistered(ObjectId id,RegistryType type)251 static bool objectIsRegistered(ObjectId id, RegistryType type)
252 {
253 UNUSED_PARAMETER(type);
254
255 if (id == 0) // null reference?
256 return true;
257
258 dvmHashTableLock(gDvm.dbgRegistry);
259 bool result = lookupId(id);
260 dvmHashTableUnlock(gDvm.dbgRegistry);
261 return result;
262 }
263 #endif
264
265 /*
266 * Convert to/from a RefTypeId.
267 *
268 * These are rarely NULL, but can be (e.g. java/lang/Object's superclass).
269 */
classObjectToRefTypeId(ClassObject * clazz)270 static RefTypeId classObjectToRefTypeId(ClassObject* clazz)
271 {
272 return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, true);
273 }
274 #if 0
275 static RefTypeId classObjectToRefTypeIdNoReg(ClassObject* clazz)
276 {
277 return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, false);
278 }
279 #endif
refTypeIdToClassObject(RefTypeId id)280 static ClassObject* refTypeIdToClassObject(RefTypeId id)
281 {
282 assert(objectIsRegistered(id, kRefTypeId) || !gDvm.debuggerConnected);
283 return (ClassObject*)(u4) id;
284 }
285
286 /*
287 * Convert to/from an ObjectId.
288 */
objectToObjectId(const Object * obj)289 static ObjectId objectToObjectId(const Object* obj)
290 {
291 return registerObject(obj, kObjectId, true);
292 }
objectToObjectIdNoReg(const Object * obj)293 static ObjectId objectToObjectIdNoReg(const Object* obj)
294 {
295 return registerObject(obj, kObjectId, false);
296 }
objectIdToObject(ObjectId id)297 static Object* objectIdToObject(ObjectId id)
298 {
299 assert(objectIsRegistered(id, kObjectId) || !gDvm.debuggerConnected);
300 return (Object*)(u4) id;
301 }
302
303 /*
304 * Register an object ID that might not have been registered previously.
305 *
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.
309 */
dvmDbgRegisterObjectId(ObjectId id)310 void dvmDbgRegisterObjectId(ObjectId id)
311 {
312 Object* obj = (Object*)(u4) id;
313 ALOGV("+++ registering %p (%s)", obj, obj->clazz->descriptor);
314 registerObject(obj, kObjectId, true);
315 }
316
317 /*
318 * Convert to/from a MethodId.
319 *
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*.
322 */
methodToMethodId(const Method * meth)323 static MethodId methodToMethodId(const Method* meth)
324 {
325 return (MethodId)(u4) meth;
326 }
methodIdToMethod(RefTypeId refTypeId,MethodId id)327 static Method* methodIdToMethod(RefTypeId refTypeId, MethodId id)
328 {
329 // TODO? verify "id" is actually a method in "refTypeId"
330 return (Method*)(u4) id;
331 }
332
333 /*
334 * Convert to/from a FieldId.
335 *
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*.
338 */
fieldToFieldId(const Field * field)339 static FieldId fieldToFieldId(const Field* field)
340 {
341 return (FieldId)(u4) field;
342 }
fieldIdToField(RefTypeId refTypeId,FieldId id)343 static Field* fieldIdToField(RefTypeId refTypeId, FieldId id)
344 {
345 // TODO? verify "id" is actually a field in "refTypeId"
346 return (Field*)(u4) id;
347 }
348
349 /*
350 * Convert to/from a FrameId.
351 *
352 * We just return a pointer to the stack frame.
353 */
frameToFrameId(const void * frame)354 static FrameId frameToFrameId(const void* frame)
355 {
356 return (FrameId)(u4) frame;
357 }
frameIdToFrame(FrameId id)358 static u4* frameIdToFrame(FrameId id)
359 {
360 return (u4*)(u4) id;
361 }
362
363
364 /*
365 * Get the invocation request state.
366 */
dvmDbgGetInvokeReq()367 DebugInvokeReq* dvmDbgGetInvokeReq()
368 {
369 return &dvmThreadSelf()->invokeReq;
370 }
371
372 /*
373 * Enable the object registry, but don't enable debugging features yet.
374 *
375 * Only called from the JDWP handler thread.
376 */
dvmDbgConnected()377 void dvmDbgConnected()
378 {
379 assert(!gDvm.debuggerConnected);
380
381 ALOGV("JDWP has attached");
382 assert(dvmHashTableNumEntries(gDvm.dbgRegistry) == 0);
383 gDvm.debuggerConnected = true;
384 }
385
386 /*
387 * Enable all debugging features, including scans for breakpoints.
388 *
389 * This is a no-op if we're already active.
390 *
391 * Only called from the JDWP handler thread.
392 */
dvmDbgActive()393 void dvmDbgActive()
394 {
395 if (gDvm.debuggerActive)
396 return;
397
398 ALOGI("Debugger is active");
399 dvmInitBreakpoints();
400 gDvm.debuggerActive = true;
401 dvmEnableAllSubMode(kSubModeDebuggerActive);
402 #if defined(WITH_JIT)
403 dvmCompilerUpdateGlobalState();
404 #endif
405 }
406
407 /*
408 * Disable debugging features.
409 *
410 * Set "debuggerConnected" to false, which disables use of the object
411 * registry.
412 *
413 * Only called from the JDWP handler thread.
414 */
dvmDbgDisconnected()415 void dvmDbgDisconnected()
416 {
417 assert(gDvm.debuggerConnected);
418
419 gDvm.debuggerActive = false;
420 dvmDisableAllSubMode(kSubModeDebuggerActive);
421 #if defined(WITH_JIT)
422 dvmCompilerUpdateGlobalState();
423 #endif
424
425 dvmHashTableLock(gDvm.dbgRegistry);
426 gDvm.debuggerConnected = false;
427
428 ALOGD("Debugger has detached; object registry had %d entries",
429 dvmHashTableNumEntries(gDvm.dbgRegistry));
430 //int i;
431 //for (i = 0; i < gDvm.dbgRegistryNext; i++)
432 // LOGVV("%4d: 0x%llx", i, gDvm.dbgRegistryTable[i]);
433
434 dvmHashTableClear(gDvm.dbgRegistry);
435 dvmHashTableUnlock(gDvm.dbgRegistry);
436 }
437
438 /*
439 * Returns "true" if a debugger is connected.
440 *
441 * Does not return "true" if it's just a DDM server.
442 */
dvmDbgIsDebuggerConnected()443 bool dvmDbgIsDebuggerConnected()
444 {
445 return gDvm.debuggerActive;
446 }
447
448 /*
449 * Get time since last debugger activity. Used when figuring out if the
450 * debugger has finished configuring us.
451 */
dvmDbgLastDebuggerActivity()452 s8 dvmDbgLastDebuggerActivity()
453 {
454 return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
455 }
456
457 /*
458 * JDWP thread is running, don't allow GC.
459 */
dvmDbgThreadRunning()460 int dvmDbgThreadRunning()
461 {
462 ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_RUNNING);
463 return static_cast<int>(oldStatus);
464 }
465
466 /*
467 * JDWP thread is idle, allow GC.
468 */
dvmDbgThreadWaiting()469 int dvmDbgThreadWaiting()
470 {
471 ThreadStatus oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
472 return static_cast<int>(oldStatus);
473 }
474
475 /*
476 * Restore state returned by Running/Waiting calls.
477 */
dvmDbgThreadContinuing(int status)478 int dvmDbgThreadContinuing(int status)
479 {
480 ThreadStatus newStatus = static_cast<ThreadStatus>(status);
481 ThreadStatus oldStatus = dvmChangeStatus(NULL, newStatus);
482 return static_cast<int>(oldStatus);
483 }
484
485 /*
486 * The debugger wants us to exit.
487 */
dvmDbgExit(int status)488 void dvmDbgExit(int status)
489 {
490 // TODO? invoke System.exit() to perform exit processing; ends up
491 // in System.exitInternal(), which can call JNI exit hook
492 ALOGI("GC lifetime allocation: %d bytes", gDvm.allocProf.allocCount);
493 if (CALC_CACHE_STATS) {
494 dvmDumpAtomicCacheStats(gDvm.instanceofCache);
495 dvmDumpBootClassPath();
496 }
497 exit(status);
498 }
499
500
501 /*
502 * ===========================================================================
503 * Class, Object, Array
504 * ===========================================================================
505 */
506
507 /*
508 * Get the class's type descriptor from a reference type ID.
509 */
dvmDbgGetClassDescriptor(RefTypeId id)510 const char* dvmDbgGetClassDescriptor(RefTypeId id)
511 {
512 ClassObject* clazz;
513
514 clazz = refTypeIdToClassObject(id);
515 return clazz->descriptor;
516 }
517
518 /*
519 * Convert a RefTypeId to an ObjectId.
520 */
dvmDbgGetClassObject(RefTypeId id)521 ObjectId dvmDbgGetClassObject(RefTypeId id)
522 {
523 ClassObject* clazz = refTypeIdToClassObject(id);
524 return objectToObjectId((Object*) clazz);
525 }
526
527 /*
528 * Return the superclass of a class (will be NULL for java/lang/Object).
529 */
dvmDbgGetSuperclass(RefTypeId id)530 RefTypeId dvmDbgGetSuperclass(RefTypeId id)
531 {
532 ClassObject* clazz = refTypeIdToClassObject(id);
533 return classObjectToRefTypeId(clazz->super);
534 }
535
536 /*
537 * Return a class's defining class loader.
538 */
dvmDbgGetClassLoader(RefTypeId id)539 RefTypeId dvmDbgGetClassLoader(RefTypeId id)
540 {
541 ClassObject* clazz = refTypeIdToClassObject(id);
542 return objectToObjectId(clazz->classLoader);
543 }
544
545 /*
546 * Return a class's access flags.
547 */
dvmDbgGetAccessFlags(RefTypeId id)548 u4 dvmDbgGetAccessFlags(RefTypeId id)
549 {
550 ClassObject* clazz = refTypeIdToClassObject(id);
551 return clazz->accessFlags & JAVA_FLAGS_MASK;
552 }
553
554 /*
555 * Is this class an interface?
556 */
dvmDbgIsInterface(RefTypeId id)557 bool dvmDbgIsInterface(RefTypeId id)
558 {
559 ClassObject* clazz = refTypeIdToClassObject(id);
560 return dvmIsInterfaceClass(clazz);
561 }
562
563 /*
564 * dvmHashForeach callback
565 */
copyRefType(void * vclazz,void * varg)566 static int copyRefType(void* vclazz, void* varg)
567 {
568 RefTypeId** pRefType = (RefTypeId**)varg;
569 **pRefType = classObjectToRefTypeId((ClassObject*) vclazz);
570 (*pRefType)++;
571 return 0;
572 }
573
574 /*
575 * Get the complete list of reference classes (i.e. all classes except
576 * the primitive types).
577 *
578 * Returns a newly-allocated buffer full of RefTypeId values.
579 */
dvmDbgGetClassList(u4 * pNumClasses,RefTypeId ** pClassRefBuf)580 void dvmDbgGetClassList(u4* pNumClasses, RefTypeId** pClassRefBuf)
581 {
582 RefTypeId* pRefType;
583
584 dvmHashTableLock(gDvm.loadedClasses);
585 *pNumClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
586 pRefType = *pClassRefBuf =
587 (RefTypeId*)malloc(sizeof(RefTypeId) * *pNumClasses);
588
589 if (dvmHashForeach(gDvm.loadedClasses, copyRefType, &pRefType) != 0) {
590 ALOGW("Warning: problem getting class list");
591 /* not really expecting this to happen */
592 } else {
593 assert(pRefType - *pClassRefBuf == (int) *pNumClasses);
594 }
595
596 dvmHashTableUnlock(gDvm.loadedClasses);
597 }
598
599 /*
600 * Get the list of reference classes "visible" to the specified class
601 * loader. A class is visible to a class loader if the ClassLoader object
602 * is the defining loader or is listed as an initiating loader.
603 *
604 * Returns a newly-allocated buffer full of RefTypeId values.
605 */
dvmDbgGetVisibleClassList(ObjectId classLoaderId,u4 * pNumClasses,RefTypeId ** pClassRefBuf)606 void dvmDbgGetVisibleClassList(ObjectId classLoaderId, u4* pNumClasses,
607 RefTypeId** pClassRefBuf)
608 {
609 Object* classLoader;
610 int numClasses = 0, maxClasses;
611
612 classLoader = objectIdToObject(classLoaderId);
613 // I don't think classLoader can be NULL, but the spec doesn't say
614
615 LOGVV("GetVisibleList: comparing to %p", classLoader);
616
617 dvmHashTableLock(gDvm.loadedClasses);
618
619 /* over-allocate the return buffer */
620 maxClasses = dvmHashTableNumEntries(gDvm.loadedClasses);
621 *pClassRefBuf = (RefTypeId*)malloc(sizeof(RefTypeId) * maxClasses);
622
623 /*
624 * Run through the list, looking for matches.
625 */
626 HashIter iter;
627 for (dvmHashIterBegin(gDvm.loadedClasses, &iter); !dvmHashIterDone(&iter);
628 dvmHashIterNext(&iter))
629 {
630 ClassObject* clazz = (ClassObject*) dvmHashIterData(&iter);
631
632 if (clazz->classLoader == classLoader ||
633 dvmLoaderInInitiatingList(clazz, classLoader))
634 {
635 LOGVV(" match '%s'", clazz->descriptor);
636 (*pClassRefBuf)[numClasses++] = classObjectToRefTypeId(clazz);
637 }
638 }
639 *pNumClasses = numClasses;
640
641 dvmHashTableUnlock(gDvm.loadedClasses);
642 }
643
644 /*
645 * Get the "JNI signature" for a class, e.g. "Ljava/lang/String;".
646 *
647 * Our class descriptors are in the correct format, so we just return that.
648 */
jniSignature(ClassObject * clazz)649 static const char* jniSignature(ClassObject* clazz)
650 {
651 return clazz->descriptor;
652 }
653
654 /*
655 * Get information about a class.
656 *
657 * If "pSignature" is not NULL, *pSignature gets the "JNI signature" of
658 * the class.
659 */
dvmDbgGetClassInfo(RefTypeId classId,u1 * pTypeTag,u4 * pStatus,const char ** pSignature)660 void dvmDbgGetClassInfo(RefTypeId classId, u1* pTypeTag, u4* pStatus,
661 const char** pSignature)
662 {
663 ClassObject* clazz = refTypeIdToClassObject(classId);
664
665 if (clazz->descriptor[0] == '[') {
666 /* generated array class */
667 *pStatus = CS_VERIFIED | CS_PREPARED;
668 *pTypeTag = TT_ARRAY;
669 } else {
670 if (clazz->status == CLASS_ERROR)
671 *pStatus = CS_ERROR;
672 else
673 *pStatus = CS_VERIFIED | CS_PREPARED | CS_INITIALIZED;
674 if (dvmIsInterfaceClass(clazz))
675 *pTypeTag = TT_INTERFACE;
676 else
677 *pTypeTag = TT_CLASS;
678 }
679 if (pSignature != NULL)
680 *pSignature = jniSignature(clazz);
681 }
682
683 /*
684 * Search the list of loaded classes for a match.
685 */
dvmDbgFindLoadedClassBySignature(const char * classDescriptor,RefTypeId * pRefTypeId)686 bool dvmDbgFindLoadedClassBySignature(const char* classDescriptor,
687 RefTypeId* pRefTypeId)
688 {
689 ClassObject* clazz;
690
691 clazz = dvmFindLoadedClass(classDescriptor);
692 if (clazz != NULL) {
693 *pRefTypeId = classObjectToRefTypeId(clazz);
694 return true;
695 } else
696 return false;
697 }
698
699
700 /*
701 * Get an object's class and "type tag".
702 */
dvmDbgGetObjectType(ObjectId objectId,u1 * pRefTypeTag,RefTypeId * pRefTypeId)703 void dvmDbgGetObjectType(ObjectId objectId, u1* pRefTypeTag,
704 RefTypeId* pRefTypeId)
705 {
706 Object* obj = objectIdToObject(objectId);
707
708 if (dvmIsArrayClass(obj->clazz))
709 *pRefTypeTag = TT_ARRAY;
710 else if (dvmIsInterfaceClass(obj->clazz))
711 *pRefTypeTag = TT_INTERFACE;
712 else
713 *pRefTypeTag = TT_CLASS;
714 *pRefTypeId = classObjectToRefTypeId(obj->clazz);
715 }
716
717 /*
718 * Get a class object's "type tag".
719 */
dvmDbgGetClassObjectType(RefTypeId refTypeId)720 u1 dvmDbgGetClassObjectType(RefTypeId refTypeId)
721 {
722 ClassObject* clazz = refTypeIdToClassObject(refTypeId);
723
724 if (dvmIsArrayClass(clazz))
725 return TT_ARRAY;
726 else if (dvmIsInterfaceClass(clazz))
727 return TT_INTERFACE;
728 else
729 return TT_CLASS;
730 }
731
732 /*
733 * Get a class' signature.
734 */
dvmDbgGetSignature(RefTypeId refTypeId)735 const char* dvmDbgGetSignature(RefTypeId refTypeId)
736 {
737 ClassObject* clazz;
738
739 clazz = refTypeIdToClassObject(refTypeId);
740 assert(clazz != NULL);
741
742 return jniSignature(clazz);
743 }
744
745 /*
746 * Get class' source file.
747 *
748 * Returns a newly-allocated string.
749 */
dvmDbgGetSourceFile(RefTypeId refTypeId)750 const char* dvmDbgGetSourceFile(RefTypeId refTypeId)
751 {
752 ClassObject* clazz;
753
754 clazz = refTypeIdToClassObject(refTypeId);
755 assert(clazz != NULL);
756
757 return clazz->sourceFile;
758 }
759
760 /*
761 * Get an object's type name. (For log message display only.)
762 */
dvmDbgGetObjectTypeName(ObjectId objectId)763 const char* dvmDbgGetObjectTypeName(ObjectId objectId)
764 {
765 if (objectId == 0)
766 return "(null)";
767
768 Object* obj = objectIdToObject(objectId);
769 return jniSignature(obj->clazz);
770 }
771
772 /*
773 * Determine whether or not a tag represents a primitive type.
774 */
isTagPrimitive(u1 tag)775 static bool isTagPrimitive(u1 tag)
776 {
777 switch (tag) {
778 case JT_BYTE:
779 case JT_CHAR:
780 case JT_FLOAT:
781 case JT_DOUBLE:
782 case JT_INT:
783 case JT_LONG:
784 case JT_SHORT:
785 case JT_VOID:
786 case JT_BOOLEAN:
787 return true;
788 case JT_ARRAY:
789 case JT_OBJECT:
790 case JT_STRING:
791 case JT_CLASS_OBJECT:
792 case JT_THREAD:
793 case JT_THREAD_GROUP:
794 case JT_CLASS_LOADER:
795 return false;
796 default:
797 ALOGE("ERROR: unhandled tag '%c'", tag);
798 assert(false);
799 return false;
800 }
801 }
802
803 /*
804 * Determine the best tag type given an object's class.
805 */
tagFromClass(ClassObject * clazz)806 static u1 tagFromClass(ClassObject* clazz)
807 {
808 if (dvmIsArrayClass(clazz))
809 return JT_ARRAY;
810
811 if (clazz == gDvm.classJavaLangString) {
812 return JT_STRING;
813 } else if (dvmIsTheClassClass(clazz)) {
814 return JT_CLASS_OBJECT;
815 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThread)) {
816 return JT_THREAD;
817 } else if (dvmInstanceof(clazz, gDvm.classJavaLangThreadGroup)) {
818 return JT_THREAD_GROUP;
819 } else if (dvmInstanceof(clazz, gDvm.classJavaLangClassLoader)) {
820 return JT_CLASS_LOADER;
821 } else {
822 return JT_OBJECT;
823 }
824 }
825
826 /*
827 * Return a basic tag value based solely on a type descriptor.
828 *
829 * The ASCII value maps directly to the JDWP tag constants, so we don't
830 * need to do much here. This does not return the fancier tags like
831 * JT_THREAD.
832 */
basicTagFromDescriptor(const char * descriptor)833 static u1 basicTagFromDescriptor(const char* descriptor)
834 {
835 return descriptor[0];
836 }
837
838 /*
839 * Objects declared to hold Object might actually hold a more specific
840 * type. The debugger may take a special interest in these (e.g. it
841 * wants to display the contents of Strings), so we want to return an
842 * appropriate tag.
843 *
844 * Null objects are tagged JT_OBJECT.
845 */
tagFromObject(const Object * obj)846 static u1 tagFromObject(const Object* obj)
847 {
848 if (obj == NULL)
849 return JT_OBJECT;
850 return tagFromClass(obj->clazz);
851 }
852
853 /*
854 * Determine the tag for an object.
855 *
856 * "objectId" may be 0 (i.e. NULL reference).
857 */
dvmDbgGetObjectTag(ObjectId objectId)858 u1 dvmDbgGetObjectTag(ObjectId objectId)
859 {
860 return tagFromObject(objectIdToObject(objectId));
861 }
862
863 /*
864 * Get the widths of the specified JDWP.Tag value.
865 */
dvmDbgGetTagWidth(int tag)866 int dvmDbgGetTagWidth(int tag)
867 {
868 switch (tag) {
869 case JT_VOID:
870 return 0;
871 case JT_BYTE:
872 case JT_BOOLEAN:
873 return 1;
874 case JT_CHAR:
875 case JT_SHORT:
876 return 2;
877 case JT_FLOAT:
878 case JT_INT:
879 return 4;
880 case JT_ARRAY:
881 case JT_OBJECT:
882 case JT_STRING:
883 case JT_THREAD:
884 case JT_THREAD_GROUP:
885 case JT_CLASS_LOADER:
886 case JT_CLASS_OBJECT:
887 return sizeof(ObjectId);
888 case JT_DOUBLE:
889 case JT_LONG:
890 return 8;
891 default:
892 ALOGE("ERROR: unhandled tag '%c'", tag);
893 assert(false);
894 return -1;
895 }
896 }
897
898
899 /*
900 * Return the length of the specified array.
901 */
dvmDbgGetArrayLength(ObjectId arrayId)902 int dvmDbgGetArrayLength(ObjectId arrayId)
903 {
904 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
905 assert(dvmIsArray(arrayObj));
906 return arrayObj->length;
907 }
908
909 /*
910 * Return a tag indicating the general type of elements in the array.
911 */
dvmDbgGetArrayElementTag(ObjectId arrayId)912 u1 dvmDbgGetArrayElementTag(ObjectId arrayId)
913 {
914 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
915
916 ClassObject* arrayClass = arrayObj->clazz;
917 u1 tag = basicTagFromDescriptor(arrayClass->descriptor + 1);
918 if (!isTagPrimitive(tag)) {
919 /* try to refine it */
920 tag = tagFromClass(arrayClass->elementClass);
921 }
922
923 return tag;
924 }
925
926 /*
927 * Copy a series of values with the specified width, changing the byte
928 * ordering to big-endian.
929 */
copyValuesToBE(u1 * out,const u1 * in,int count,int width)930 static void copyValuesToBE(u1* out, const u1* in, int count, int width)
931 {
932 int i;
933
934 switch (width) {
935 case 1:
936 memcpy(out, in, count);
937 break;
938 case 2:
939 for (i = 0; i < count; i++)
940 *(((u2*) out)+i) = get2BE(in + i*2);
941 break;
942 case 4:
943 for (i = 0; i < count; i++)
944 *(((u4*) out)+i) = get4BE(in + i*4);
945 break;
946 case 8:
947 for (i = 0; i < count; i++)
948 *(((u8*) out)+i) = get8BE(in + i*8);
949 break;
950 default:
951 assert(false);
952 }
953 }
954
955 /*
956 * Copy a series of values with the specified width, changing the
957 * byte order from big-endian.
958 */
copyValuesFromBE(u1 * out,const u1 * in,int count,int width)959 static void copyValuesFromBE(u1* out, const u1* in, int count, int width)
960 {
961 int i;
962
963 switch (width) {
964 case 1:
965 memcpy(out, in, count);
966 break;
967 case 2:
968 for (i = 0; i < count; i++)
969 set2BE(out + i*2, *((u2*)in + i));
970 break;
971 case 4:
972 for (i = 0; i < count; i++)
973 set4BE(out + i*4, *((u4*)in + i));
974 break;
975 case 8:
976 for (i = 0; i < count; i++)
977 set8BE(out + i*8, *((u8*)in + i));
978 break;
979 default:
980 assert(false);
981 }
982 }
983
984 /*
985 * Output a piece of an array to the reply buffer.
986 *
987 * Returns "false" if something looks fishy.
988 */
dvmDbgOutputArray(ObjectId arrayId,int firstIndex,int count,ExpandBuf * pReply)989 bool dvmDbgOutputArray(ObjectId arrayId, int firstIndex, int count,
990 ExpandBuf* pReply)
991 {
992 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
993 const u1* data = (const u1*)arrayObj->contents;
994 u1 tag;
995
996 assert(dvmIsArray(arrayObj));
997
998 if (firstIndex + count > (int)arrayObj->length) {
999 ALOGW("Request for index=%d + count=%d excceds length=%d",
1000 firstIndex, count, arrayObj->length);
1001 return false;
1002 }
1003
1004 tag = basicTagFromDescriptor(arrayObj->clazz->descriptor + 1);
1005
1006 if (isTagPrimitive(tag)) {
1007 int width = dvmDbgGetTagWidth(tag);
1008 u1* outBuf;
1009
1010 outBuf = expandBufAddSpace(pReply, count * width);
1011
1012 copyValuesToBE(outBuf, data + firstIndex*width, count, width);
1013 } else {
1014 Object** pObjects;
1015 int i;
1016
1017 pObjects = (Object**) data;
1018 pObjects += firstIndex;
1019
1020 ALOGV(" --> copying %d object IDs", count);
1021 //assert(tag == JT_OBJECT); // could be object or "refined" type
1022
1023 for (i = 0; i < count; i++, pObjects++) {
1024 u1 thisTag;
1025 if (*pObjects != NULL)
1026 thisTag = tagFromObject(*pObjects);
1027 else
1028 thisTag = tag;
1029 expandBufAdd1(pReply, thisTag);
1030 expandBufAddObjectId(pReply, objectToObjectId(*pObjects));
1031 }
1032 }
1033
1034 return true;
1035 }
1036
1037 /*
1038 * Set a range of elements in an array from the data in "buf".
1039 */
dvmDbgSetArrayElements(ObjectId arrayId,int firstIndex,int count,const u1 * buf)1040 bool dvmDbgSetArrayElements(ObjectId arrayId, int firstIndex, int count,
1041 const u1* buf)
1042 {
1043 ArrayObject* arrayObj = (ArrayObject*) objectIdToObject(arrayId);
1044 u1* data = (u1*)arrayObj->contents;
1045 u1 tag;
1046
1047 assert(dvmIsArray(arrayObj));
1048
1049 if (firstIndex + count > (int)arrayObj->length) {
1050 ALOGW("Attempt to set index=%d + count=%d excceds length=%d",
1051 firstIndex, count, arrayObj->length);
1052 return false;
1053 }
1054
1055 tag = basicTagFromDescriptor(arrayObj->clazz->descriptor + 1);
1056
1057 if (isTagPrimitive(tag)) {
1058 int width = dvmDbgGetTagWidth(tag);
1059
1060 ALOGV(" --> setting %d '%c' width=%d", count, tag, width);
1061
1062 copyValuesFromBE(data + firstIndex*width, buf, count, width);
1063 } else {
1064 Object** pObjects;
1065 int i;
1066
1067 pObjects = (Object**) data;
1068 pObjects += firstIndex;
1069
1070 ALOGV(" --> setting %d objects", count);
1071
1072 /* should do array type check here */
1073 for (i = 0; i < count; i++) {
1074 ObjectId id = dvmReadObjectId(&buf);
1075 *pObjects++ = objectIdToObject(id);
1076 }
1077 }
1078
1079 return true;
1080 }
1081
1082 /*
1083 * Create a new string.
1084 *
1085 * The only place the reference will be held in the VM is in our registry.
1086 */
dvmDbgCreateString(const char * str)1087 ObjectId dvmDbgCreateString(const char* str)
1088 {
1089 StringObject* strObj;
1090
1091 strObj = dvmCreateStringFromCstr(str);
1092 dvmReleaseTrackedAlloc((Object*) strObj, NULL);
1093 return objectToObjectId((Object*) strObj);
1094 }
1095
1096 /*
1097 * Allocate a new object of the specified type.
1098 *
1099 * Add it to the registry to prevent it from being GCed.
1100 */
dvmDbgCreateObject(RefTypeId classId)1101 ObjectId dvmDbgCreateObject(RefTypeId classId)
1102 {
1103 ClassObject* clazz = refTypeIdToClassObject(classId);
1104 Object* newObj = dvmAllocObject(clazz, ALLOC_DEFAULT);
1105 dvmReleaseTrackedAlloc(newObj, NULL);
1106 return objectToObjectId(newObj);
1107 }
1108
1109 /*
1110 * Allocate a new array object of the specified type and length. The
1111 * type is the array type, not the element type.
1112 *
1113 * Add it to the registry to prevent it from being GCed.
1114 */
dvmDbgCreateArrayObject(RefTypeId arrayTypeId,u4 length)1115 ObjectId dvmDbgCreateArrayObject(RefTypeId arrayTypeId, u4 length)
1116 {
1117 ClassObject* clazz = refTypeIdToClassObject(arrayTypeId);
1118 Object* newObj = (Object*) dvmAllocArrayByClass(clazz, length, ALLOC_DEFAULT);
1119 dvmReleaseTrackedAlloc(newObj, NULL);
1120 return objectToObjectId(newObj);
1121 }
1122
1123 /*
1124 * Determine if "instClassId" is an instance of "classId".
1125 */
dvmDbgMatchType(RefTypeId instClassId,RefTypeId classId)1126 bool dvmDbgMatchType(RefTypeId instClassId, RefTypeId classId)
1127 {
1128 ClassObject* instClazz = refTypeIdToClassObject(instClassId);
1129 ClassObject* clazz = refTypeIdToClassObject(classId);
1130
1131 return dvmInstanceof(instClazz, clazz);
1132 }
1133
1134
1135 /*
1136 * ===========================================================================
1137 * Method and Field
1138 * ===========================================================================
1139 */
1140
1141 /*
1142 * Get the method name from a MethodId.
1143 */
dvmDbgGetMethodName(RefTypeId refTypeId,MethodId id)1144 const char* dvmDbgGetMethodName(RefTypeId refTypeId, MethodId id)
1145 {
1146 Method* meth;
1147
1148 meth = methodIdToMethod(refTypeId, id);
1149 return meth->name;
1150 }
1151
1152 /*
1153 * Augment the access flags for synthetic methods and fields by setting
1154 * the (as described by the spec) "0xf0000000 bit". Also, strip out any
1155 * flags not specified by the Java programming language.
1156 */
augmentedAccessFlags(u4 accessFlags)1157 static u4 augmentedAccessFlags(u4 accessFlags)
1158 {
1159 accessFlags &= JAVA_FLAGS_MASK;
1160
1161 if ((accessFlags & ACC_SYNTHETIC) != 0) {
1162 return accessFlags | 0xf0000000;
1163 } else {
1164 return accessFlags;
1165 }
1166 }
1167
1168 /*
1169 * For ReferenceType.Fields and ReferenceType.FieldsWithGeneric:
1170 * output all fields declared by the class. Inherited fields are
1171 * not included.
1172 */
dvmDbgOutputAllFields(RefTypeId refTypeId,bool withGeneric,ExpandBuf * pReply)1173 void dvmDbgOutputAllFields(RefTypeId refTypeId, bool withGeneric,
1174 ExpandBuf* pReply)
1175 {
1176 ClassObject* clazz = refTypeIdToClassObject(refTypeId);
1177 assert(clazz != NULL);
1178
1179 u4 declared = clazz->sfieldCount + clazz->ifieldCount;
1180 expandBufAdd4BE(pReply, declared);
1181
1182 for (int i = 0; i < clazz->sfieldCount; i++) {
1183 Field* field = &clazz->sfields[i];
1184 expandBufAddFieldId(pReply, fieldToFieldId(field));
1185 expandBufAddUtf8String(pReply, (const u1*) field->name);
1186 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1187 if (withGeneric) {
1188 static const u1 genericSignature[1] = "";
1189 expandBufAddUtf8String(pReply, genericSignature);
1190 }
1191 expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
1192 }
1193 for (int i = 0; i < clazz->ifieldCount; i++) {
1194 Field* field = &clazz->ifields[i];
1195 expandBufAddFieldId(pReply, fieldToFieldId(field));
1196 expandBufAddUtf8String(pReply, (const u1*) field->name);
1197 expandBufAddUtf8String(pReply, (const u1*) field->signature);
1198 if (withGeneric) {
1199 static const u1 genericSignature[1] = "";
1200 expandBufAddUtf8String(pReply, genericSignature);
1201 }
1202 expandBufAdd4BE(pReply, augmentedAccessFlags(field->accessFlags));
1203 }
1204 }
1205
1206 /*
1207 * For ReferenceType.Methods and ReferenceType.MethodsWithGeneric:
1208 * output all methods declared by the class. Inherited methods are
1209 * not included.
1210 */
dvmDbgOutputAllMethods(RefTypeId refTypeId,bool withGeneric,ExpandBuf * pReply)1211 void dvmDbgOutputAllMethods(RefTypeId refTypeId, bool withGeneric,
1212 ExpandBuf* pReply)
1213 {
1214 DexStringCache stringCache;
1215 static const u1 genericSignature[1] = "";
1216 ClassObject* clazz;
1217 Method* meth;
1218 u4 declared;
1219 int i;
1220
1221 dexStringCacheInit(&stringCache);
1222
1223 clazz = refTypeIdToClassObject(refTypeId);
1224 assert(clazz != NULL);
1225
1226 declared = clazz->directMethodCount + clazz->virtualMethodCount;
1227 expandBufAdd4BE(pReply, declared);
1228
1229 for (i = 0; i < clazz->directMethodCount; i++) {
1230 meth = &clazz->directMethods[i];
1231
1232 expandBufAddMethodId(pReply, methodToMethodId(meth));
1233 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1234
1235 expandBufAddUtf8String(pReply,
1236 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1237 &stringCache));
1238
1239 if (withGeneric)
1240 expandBufAddUtf8String(pReply, genericSignature);
1241 expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
1242 }
1243 for (i = 0; i < clazz->virtualMethodCount; i++) {
1244 meth = &clazz->virtualMethods[i];
1245
1246 expandBufAddMethodId(pReply, methodToMethodId(meth));
1247 expandBufAddUtf8String(pReply, (const u1*) meth->name);
1248
1249 expandBufAddUtf8String(pReply,
1250 (const u1*) dexProtoGetMethodDescriptor(&meth->prototype,
1251 &stringCache));
1252
1253 if (withGeneric)
1254 expandBufAddUtf8String(pReply, genericSignature);
1255 expandBufAdd4BE(pReply, augmentedAccessFlags(meth->accessFlags));
1256 }
1257
1258 dexStringCacheRelease(&stringCache);
1259 }
1260
1261 /*
1262 * Output all interfaces directly implemented by the class.
1263 */
dvmDbgOutputAllInterfaces(RefTypeId refTypeId,ExpandBuf * pReply)1264 void dvmDbgOutputAllInterfaces(RefTypeId refTypeId, ExpandBuf* pReply)
1265 {
1266 ClassObject* clazz;
1267 int i, count;
1268
1269 clazz = refTypeIdToClassObject(refTypeId);
1270 assert(clazz != NULL);
1271
1272 count = clazz->interfaceCount;
1273 expandBufAdd4BE(pReply, count);
1274 for (i = 0; i < count; i++) {
1275 ClassObject* iface = clazz->interfaces[i];
1276 expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
1277 }
1278 }
1279
1280 struct DebugCallbackContext {
1281 int numItems;
1282 ExpandBuf* pReply;
1283 // used by locals table
1284 bool withGeneric;
1285 };
1286
lineTablePositionsCb(void * cnxt,u4 address,u4 lineNum)1287 static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
1288 {
1289 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1290
1291 expandBufAdd8BE(pContext->pReply, address);
1292 expandBufAdd4BE(pContext->pReply, lineNum);
1293 pContext->numItems++;
1294
1295 return 0;
1296 }
1297
1298 /*
1299 * For Method.LineTable: output the line table.
1300 *
1301 * Note we operate in Dalvik's 16-bit units rather than bytes.
1302 */
dvmDbgOutputLineTable(RefTypeId refTypeId,MethodId methodId,ExpandBuf * pReply)1303 void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
1304 ExpandBuf* pReply)
1305 {
1306 Method* method;
1307 u8 start, end;
1308 DebugCallbackContext context;
1309
1310 memset (&context, 0, sizeof(DebugCallbackContext));
1311
1312 method = methodIdToMethod(refTypeId, methodId);
1313 if (dvmIsNativeMethod(method)) {
1314 start = (u8) -1;
1315 end = (u8) -1;
1316 } else {
1317 start = 0;
1318 end = dvmGetMethodInsnsSize(method);
1319 }
1320
1321 expandBufAdd8BE(pReply, start);
1322 expandBufAdd8BE(pReply, end);
1323
1324 // Add numLines later
1325 size_t numLinesOffset = expandBufGetLength(pReply);
1326 expandBufAdd4BE(pReply, 0);
1327
1328 context.pReply = pReply;
1329
1330 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1331 dvmGetMethodCode(method),
1332 method->clazz->descriptor,
1333 method->prototype.protoIdx,
1334 method->accessFlags,
1335 lineTablePositionsCb, NULL, &context);
1336
1337 set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
1338 }
1339
1340 /*
1341 * Eclipse appears to expect that the "this" reference is in slot zero.
1342 * If it's not, the "variables" display will show two copies of "this",
1343 * possibly because it gets "this" from SF.ThisObject and then displays
1344 * all locals with nonzero slot numbers.
1345 *
1346 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
1347 * SF.GetValues / SF.SetValues we map them back.
1348 */
tweakSlot(int slot,const char * name)1349 static int tweakSlot(int slot, const char* name)
1350 {
1351 int newSlot = slot;
1352
1353 if (strcmp(name, "this") == 0) // only remap "this" ptr
1354 newSlot = 0;
1355 else if (slot == 0) // always remap slot 0
1356 newSlot = kSlot0Sub;
1357
1358 ALOGV("untweak: %d to %d", slot, newSlot);
1359 return newSlot;
1360 }
1361
1362 /*
1363 * Reverse Eclipse hack.
1364 */
untweakSlot(int slot,const void * framePtr)1365 static int untweakSlot(int slot, const void* framePtr)
1366 {
1367 int newSlot = slot;
1368
1369 if (slot == kSlot0Sub) {
1370 newSlot = 0;
1371 } else if (slot == 0) {
1372 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
1373 const Method* method = saveArea->method;
1374 newSlot = method->registersSize - method->insSize;
1375 }
1376
1377 ALOGV("untweak: %d to %d", slot, newSlot);
1378 return newSlot;
1379 }
1380
variableTableCb(void * cnxt,u2 reg,u4 startAddress,u4 endAddress,const char * name,const char * descriptor,const char * signature)1381 static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
1382 u4 endAddress, const char *name, const char *descriptor,
1383 const char *signature)
1384 {
1385 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1386
1387 reg = (u2) tweakSlot(reg, name);
1388
1389 ALOGV(" %2d: %d(%d) '%s' '%s' slot=%d",
1390 pContext->numItems, startAddress, endAddress - startAddress,
1391 name, descriptor, reg);
1392
1393 expandBufAdd8BE(pContext->pReply, startAddress);
1394 expandBufAddUtf8String(pContext->pReply, (const u1*)name);
1395 expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
1396 if (pContext->withGeneric) {
1397 expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
1398 }
1399 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
1400 expandBufAdd4BE(pContext->pReply, reg);
1401
1402 pContext->numItems++;
1403 }
1404
1405 /*
1406 * For Method.VariableTable[WithGeneric]: output information about local
1407 * variables for the specified method.
1408 */
dvmDbgOutputVariableTable(RefTypeId refTypeId,MethodId methodId,bool withGeneric,ExpandBuf * pReply)1409 void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
1410 bool withGeneric, ExpandBuf* pReply)
1411 {
1412 Method* method;
1413 DebugCallbackContext context;
1414
1415 memset (&context, 0, sizeof(DebugCallbackContext));
1416
1417 method = methodIdToMethod(refTypeId, methodId);
1418
1419 expandBufAdd4BE(pReply, method->insSize);
1420
1421 // Add numLocals later
1422 size_t numLocalsOffset = expandBufGetLength(pReply);
1423 expandBufAdd4BE(pReply, 0);
1424
1425 context.pReply = pReply;
1426 context.withGeneric = withGeneric;
1427 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1428 dvmGetMethodCode(method),
1429 method->clazz->descriptor,
1430 method->prototype.protoIdx,
1431 method->accessFlags,
1432 NULL, variableTableCb, &context);
1433
1434 set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
1435 }
1436
1437 /*
1438 * Get the basic tag for an instance field.
1439 */
dvmDbgGetFieldBasicTag(ObjectId objId,FieldId fieldId)1440 u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId)
1441 {
1442 Object* obj = objectIdToObject(objId);
1443 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1444 const Field* field = fieldIdToField(classId, fieldId);
1445 return basicTagFromDescriptor(field->signature);
1446 }
1447
1448 /*
1449 * Get the basic tag for a static field.
1450 */
dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId,FieldId fieldId)1451 u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId)
1452 {
1453 const Field* field = fieldIdToField(refTypeId, fieldId);
1454 return basicTagFromDescriptor(field->signature);
1455 }
1456
1457
1458 /*
1459 * Copy the value of a static field into the output buffer, preceded
1460 * by an appropriate tag. The tag is based on the value held by the
1461 * field, not the field's type.
1462 */
dvmDbgGetFieldValue(ObjectId objectId,FieldId fieldId,ExpandBuf * pReply)1463 void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply)
1464 {
1465 Object* obj = objectIdToObject(objectId);
1466 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1467 InstField* ifield = (InstField*) fieldIdToField(classId, fieldId);
1468 u1 tag = basicTagFromDescriptor(ifield->signature);
1469
1470 if (tag == JT_ARRAY || tag == JT_OBJECT) {
1471 Object* objVal = dvmGetFieldObject(obj, ifield->byteOffset);
1472 tag = tagFromObject(objVal);
1473 expandBufAdd1(pReply, tag);
1474 expandBufAddObjectId(pReply, objectToObjectId(objVal));
1475 ALOGV(" --> ifieldId %x --> tag '%c' %p", fieldId, tag, objVal);
1476 } else {
1477 ALOGV(" --> ifieldId %x --> tag '%c'", fieldId, tag);
1478 expandBufAdd1(pReply, tag);
1479
1480 switch (tag) {
1481 case JT_BOOLEAN:
1482 expandBufAdd1(pReply, dvmGetFieldBoolean(obj, ifield->byteOffset));
1483 break;
1484 case JT_BYTE:
1485 expandBufAdd1(pReply, dvmGetFieldByte(obj, ifield->byteOffset));
1486 break;
1487 case JT_SHORT:
1488 expandBufAdd2BE(pReply, dvmGetFieldShort(obj, ifield->byteOffset));
1489 break;
1490 case JT_CHAR:
1491 expandBufAdd2BE(pReply, dvmGetFieldChar(obj, ifield->byteOffset));
1492 break;
1493 case JT_INT:
1494 case JT_FLOAT:
1495 expandBufAdd4BE(pReply, dvmGetFieldInt(obj, ifield->byteOffset));
1496 break;
1497 case JT_LONG:
1498 case JT_DOUBLE:
1499 expandBufAdd8BE(pReply, dvmGetFieldLong(obj, ifield->byteOffset));
1500 break;
1501 default:
1502 ALOGE("ERROR: unhandled field type '%s'", ifield->signature);
1503 assert(false);
1504 break;
1505 }
1506 }
1507 }
1508
1509 /*
1510 * Set the value of the specified field.
1511 */
dvmDbgSetFieldValue(ObjectId objectId,FieldId fieldId,u8 value,int width)1512 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
1513 int width)
1514 {
1515 Object* obj = objectIdToObject(objectId);
1516 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1517 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1518
1519 switch (field->signature[0]) {
1520 case JT_BOOLEAN:
1521 assert(width == 1);
1522 dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
1523 break;
1524 case JT_BYTE:
1525 assert(width == 1);
1526 dvmSetFieldInt(obj, field->byteOffset, value);
1527 break;
1528 case JT_SHORT:
1529 case JT_CHAR:
1530 assert(width == 2);
1531 dvmSetFieldInt(obj, field->byteOffset, value);
1532 break;
1533 case JT_INT:
1534 case JT_FLOAT:
1535 assert(width == 4);
1536 dvmSetFieldInt(obj, field->byteOffset, value);
1537 break;
1538 case JT_ARRAY:
1539 case JT_OBJECT:
1540 assert(width == sizeof(ObjectId));
1541 dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
1542 break;
1543 case JT_DOUBLE:
1544 case JT_LONG:
1545 assert(width == 8);
1546 dvmSetFieldLong(obj, field->byteOffset, value);
1547 break;
1548 default:
1549 ALOGE("ERROR: unhandled class type '%s'", field->signature);
1550 assert(false);
1551 break;
1552 }
1553 }
1554
1555 /*
1556 * Copy the value of a static field into the output buffer, preceded
1557 * by an appropriate tag. The tag is based on the value held by the
1558 * field, not the field's type.
1559 */
dvmDbgGetStaticFieldValue(RefTypeId refTypeId,FieldId fieldId,ExpandBuf * pReply)1560 void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1561 ExpandBuf* pReply)
1562 {
1563 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1564 u1 tag = basicTagFromDescriptor(sfield->signature);
1565
1566 if (tag == JT_ARRAY || tag == JT_OBJECT) {
1567 Object* objVal = dvmGetStaticFieldObject(sfield);
1568 tag = tagFromObject(objVal);
1569 expandBufAdd1(pReply, tag);
1570 expandBufAddObjectId(pReply, objectToObjectId(objVal));
1571 ALOGV(" --> sfieldId %x --> tag '%c' %p", fieldId, tag, objVal);
1572 } else {
1573 JValue value;
1574
1575 ALOGV(" --> sfieldId %x --> tag '%c'", fieldId, tag);
1576 expandBufAdd1(pReply, tag);
1577
1578 switch (tag) {
1579 case JT_BOOLEAN:
1580 expandBufAdd1(pReply, dvmGetStaticFieldBoolean(sfield));
1581 break;
1582 case JT_BYTE:
1583 expandBufAdd1(pReply, dvmGetStaticFieldByte(sfield));
1584 break;
1585 case JT_SHORT:
1586 expandBufAdd2BE(pReply, dvmGetStaticFieldShort(sfield));
1587 break;
1588 case JT_CHAR:
1589 expandBufAdd2BE(pReply, dvmGetStaticFieldChar(sfield));
1590 break;
1591 case JT_INT:
1592 expandBufAdd4BE(pReply, dvmGetStaticFieldInt(sfield));
1593 break;
1594 case JT_FLOAT:
1595 value.f = dvmGetStaticFieldFloat(sfield);
1596 expandBufAdd4BE(pReply, value.i);
1597 break;
1598 case JT_LONG:
1599 expandBufAdd8BE(pReply, dvmGetStaticFieldLong(sfield));
1600 break;
1601 case JT_DOUBLE:
1602 value.d = dvmGetStaticFieldDouble(sfield);
1603 expandBufAdd8BE(pReply, value.j);
1604 break;
1605 default:
1606 ALOGE("ERROR: unhandled field type '%s'", sfield->signature);
1607 assert(false);
1608 break;
1609 }
1610 }
1611 }
1612
1613 /*
1614 * Set the value of a static field.
1615 */
dvmDbgSetStaticFieldValue(RefTypeId refTypeId,FieldId fieldId,u8 rawValue,int width)1616 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1617 u8 rawValue, int width)
1618 {
1619 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1620 Object* objVal;
1621 JValue value;
1622
1623 value.j = rawValue;
1624
1625 switch (sfield->signature[0]) {
1626 case JT_BOOLEAN:
1627 assert(width == 1);
1628 dvmSetStaticFieldBoolean(sfield, value.z);
1629 break;
1630 case JT_BYTE:
1631 assert(width == 1);
1632 dvmSetStaticFieldByte(sfield, value.b);
1633 break;
1634 case JT_SHORT:
1635 assert(width == 2);
1636 dvmSetStaticFieldShort(sfield, value.s);
1637 break;
1638 case JT_CHAR:
1639 assert(width == 2);
1640 dvmSetStaticFieldChar(sfield, value.c);
1641 break;
1642 case JT_INT:
1643 assert(width == 4);
1644 dvmSetStaticFieldInt(sfield, value.i);
1645 break;
1646 case JT_FLOAT:
1647 assert(width == 4);
1648 dvmSetStaticFieldFloat(sfield, value.f);
1649 break;
1650 case JT_ARRAY:
1651 case JT_OBJECT:
1652 assert(width == sizeof(ObjectId));
1653 objVal = objectIdToObject(rawValue);
1654 dvmSetStaticFieldObject(sfield, objVal);
1655 break;
1656 case JT_LONG:
1657 assert(width == 8);
1658 dvmSetStaticFieldLong(sfield, value.j);
1659 break;
1660 case JT_DOUBLE:
1661 assert(width == 8);
1662 dvmSetStaticFieldDouble(sfield, value.d);
1663 break;
1664 default:
1665 ALOGE("ERROR: unhandled class type '%s'", sfield->signature);
1666 assert(false);
1667 break;
1668 }
1669 }
1670
1671 /*
1672 * Convert a string object to a UTF-8 string.
1673 *
1674 * Returns a newly-allocated string.
1675 */
dvmDbgStringToUtf8(ObjectId strId)1676 char* dvmDbgStringToUtf8(ObjectId strId)
1677 {
1678 StringObject* strObj = (StringObject*) objectIdToObject(strId);
1679
1680 return dvmCreateCstrFromString(strObj);
1681 }
1682
1683
1684 /*
1685 * ===========================================================================
1686 * Thread and ThreadGroup
1687 * ===========================================================================
1688 */
1689
1690 /*
1691 * Convert a thread object to a Thread ptr.
1692 *
1693 * This currently requires running through the list of threads and finding
1694 * a match.
1695 *
1696 * IMPORTANT: grab gDvm.threadListLock before calling here.
1697 */
threadObjToThread(Object * threadObj)1698 static Thread* threadObjToThread(Object* threadObj)
1699 {
1700 Thread* thread;
1701
1702 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1703 if (thread->threadObj == threadObj)
1704 break;
1705 }
1706
1707 return thread;
1708 }
1709
1710 /*
1711 * Get the status and suspend state of a thread.
1712 */
dvmDbgGetThreadStatus(ObjectId threadId,u4 * pThreadStatus,u4 * pSuspendStatus)1713 bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
1714 u4* pSuspendStatus)
1715 {
1716 Object* threadObj;
1717 Thread* thread;
1718 bool result = false;
1719
1720 threadObj = objectIdToObject(threadId);
1721 assert(threadObj != NULL);
1722
1723 /* lock the thread list, so the thread doesn't vanish while we work */
1724 dvmLockThreadList(NULL);
1725
1726 thread = threadObjToThread(threadObj);
1727 if (thread == NULL)
1728 goto bail;
1729
1730 switch (thread->status) {
1731 case THREAD_ZOMBIE: *pThreadStatus = TS_ZOMBIE; break;
1732 case THREAD_RUNNING: *pThreadStatus = TS_RUNNING; break;
1733 case THREAD_TIMED_WAIT: *pThreadStatus = TS_SLEEPING; break;
1734 case THREAD_MONITOR: *pThreadStatus = TS_MONITOR; break;
1735 case THREAD_WAIT: *pThreadStatus = TS_WAIT; break;
1736 case THREAD_INITIALIZING: *pThreadStatus = TS_ZOMBIE; break;
1737 case THREAD_STARTING: *pThreadStatus = TS_ZOMBIE; break;
1738 case THREAD_NATIVE: *pThreadStatus = TS_RUNNING; break;
1739 case THREAD_VMWAIT: *pThreadStatus = TS_WAIT; break;
1740 case THREAD_SUSPENDED: *pThreadStatus = TS_RUNNING; break;
1741 default:
1742 assert(false);
1743 *pThreadStatus = THREAD_ZOMBIE;
1744 break;
1745 }
1746
1747 if (dvmIsSuspended(thread))
1748 *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
1749 else
1750 *pSuspendStatus = 0;
1751
1752 result = true;
1753
1754 bail:
1755 dvmUnlockThreadList();
1756 return result;
1757 }
1758
1759 /*
1760 * Get the thread's suspend count.
1761 */
dvmDbgGetThreadSuspendCount(ObjectId threadId)1762 u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
1763 {
1764 Object* threadObj;
1765 Thread* thread;
1766 u4 result = 0;
1767
1768 threadObj = objectIdToObject(threadId);
1769 assert(threadObj != NULL);
1770
1771 /* lock the thread list, so the thread doesn't vanish while we work */
1772 dvmLockThreadList(NULL);
1773
1774 thread = threadObjToThread(threadObj);
1775 if (thread == NULL)
1776 goto bail;
1777
1778 result = thread->suspendCount;
1779
1780 bail:
1781 dvmUnlockThreadList();
1782 return result;
1783 }
1784
1785 /*
1786 * Determine whether or not a thread exists in the VM's thread list.
1787 *
1788 * Returns "true" if the thread exists.
1789 */
dvmDbgThreadExists(ObjectId threadId)1790 bool dvmDbgThreadExists(ObjectId threadId)
1791 {
1792 Object* threadObj;
1793 Thread* thread;
1794 bool result;
1795
1796 threadObj = objectIdToObject(threadId);
1797 assert(threadObj != NULL);
1798
1799 /* lock the thread list, so the thread doesn't vanish while we work */
1800 dvmLockThreadList(NULL);
1801
1802 thread = threadObjToThread(threadObj);
1803 if (thread == NULL)
1804 result = false;
1805 else
1806 result = true;
1807
1808 dvmUnlockThreadList();
1809 return result;
1810 }
1811
1812 /*
1813 * Determine whether or not a thread is suspended.
1814 *
1815 * Returns "false" if the thread is running or doesn't exist.
1816 */
dvmDbgIsSuspended(ObjectId threadId)1817 bool dvmDbgIsSuspended(ObjectId threadId)
1818 {
1819 Object* threadObj;
1820 Thread* thread;
1821 bool result = false;
1822
1823 threadObj = objectIdToObject(threadId);
1824 assert(threadObj != NULL);
1825
1826 /* lock the thread list, so the thread doesn't vanish while we work */
1827 dvmLockThreadList(NULL);
1828
1829 thread = threadObjToThread(threadObj);
1830 if (thread == NULL)
1831 goto bail;
1832
1833 result = dvmIsSuspended(thread);
1834
1835 bail:
1836 dvmUnlockThreadList();
1837 return result;
1838 }
1839
1840 /*
1841 * Return the ObjectId for the "system" thread group.
1842 */
dvmDbgGetSystemThreadGroupId()1843 ObjectId dvmDbgGetSystemThreadGroupId()
1844 {
1845 Object* groupObj = dvmGetSystemThreadGroup();
1846 return objectToObjectId(groupObj);
1847 }
1848
1849 /*
1850 * Return the ObjectId for the "main" thread group.
1851 */
dvmDbgGetMainThreadGroupId()1852 ObjectId dvmDbgGetMainThreadGroupId()
1853 {
1854 Object* groupObj = dvmGetMainThreadGroup();
1855 return objectToObjectId(groupObj);
1856 }
1857
1858 /*
1859 * Get the name of a thread.
1860 *
1861 * Returns a newly-allocated string.
1862 */
dvmDbgGetThreadName(ObjectId threadId)1863 char* dvmDbgGetThreadName(ObjectId threadId)
1864 {
1865 Object* threadObj;
1866 StringObject* nameStr;
1867 char* str;
1868 char* result;
1869
1870 threadObj = objectIdToObject(threadId);
1871 assert(threadObj != NULL);
1872
1873 nameStr = (StringObject*) dvmGetFieldObject(threadObj,
1874 gDvm.offJavaLangThread_name);
1875 str = dvmCreateCstrFromString(nameStr);
1876 result = (char*) malloc(strlen(str) + 20);
1877
1878 /* lock the thread list, so the thread doesn't vanish while we work */
1879 dvmLockThreadList(NULL);
1880 Thread* thread = threadObjToThread(threadObj);
1881 if (thread != NULL)
1882 sprintf(result, "<%d> %s", thread->threadId, str);
1883 else
1884 sprintf(result, "%s", str);
1885 dvmUnlockThreadList();
1886
1887 free(str);
1888 return result;
1889 }
1890
1891 /*
1892 * Get a thread's group.
1893 */
dvmDbgGetThreadGroup(ObjectId threadId)1894 ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
1895 {
1896 Object* threadObj;
1897 Object* group;
1898
1899 threadObj = objectIdToObject(threadId);
1900 assert(threadObj != NULL);
1901
1902 group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
1903 return objectToObjectId(group);
1904 }
1905
1906
1907 /*
1908 * Get the name of a thread group.
1909 *
1910 * Returns a newly-allocated string.
1911 */
dvmDbgGetThreadGroupName(ObjectId threadGroupId)1912 char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
1913 {
1914 Object* threadGroup;
1915 StringObject* nameStr;
1916
1917 threadGroup = objectIdToObject(threadGroupId);
1918 assert(threadGroup != NULL);
1919
1920 nameStr = (StringObject*)
1921 dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_name);
1922 return dvmCreateCstrFromString(nameStr);
1923 }
1924
1925 /*
1926 * Get the parent of a thread group.
1927 *
1928 * Returns a newly-allocated string.
1929 */
dvmDbgGetThreadGroupParent(ObjectId threadGroupId)1930 ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
1931 {
1932 Object* threadGroup;
1933 Object* parent;
1934
1935 threadGroup = objectIdToObject(threadGroupId);
1936 assert(threadGroup != NULL);
1937
1938 parent = dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_parent);
1939 return objectToObjectId(parent);
1940 }
1941
1942 /*
1943 * Get the list of threads in the thread group.
1944 *
1945 * We do this by running through the full list of threads and returning
1946 * the ones that have the ThreadGroup object as their owner.
1947 *
1948 * If threadGroupId is set to "kAllThreads", we ignore the group field and
1949 * return all threads.
1950 *
1951 * The caller must free "*ppThreadIds".
1952 */
dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,ObjectId ** ppThreadIds,u4 * pThreadCount)1953 void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
1954 ObjectId** ppThreadIds, u4* pThreadCount)
1955 {
1956 Object* targetThreadGroup = NULL;
1957 Thread* thread;
1958 int count;
1959
1960 if (threadGroupId != THREAD_GROUP_ALL) {
1961 targetThreadGroup = objectIdToObject(threadGroupId);
1962 assert(targetThreadGroup != NULL);
1963 }
1964
1965 dvmLockThreadList(NULL);
1966
1967 thread = gDvm.threadList;
1968 count = 0;
1969 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1970 Object* group;
1971
1972 /* Skip over the JDWP support thread. Some debuggers
1973 * get bent out of shape when they can't suspend and
1974 * query all threads, so it's easier if we just don't
1975 * tell them about us.
1976 */
1977 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
1978 continue;
1979
1980 /* This thread is currently being created, and isn't ready
1981 * to be seen by the debugger yet.
1982 */
1983 if (thread->threadObj == NULL)
1984 continue;
1985
1986 group = dvmGetFieldObject(thread->threadObj,
1987 gDvm.offJavaLangThread_group);
1988 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
1989 count++;
1990 }
1991
1992 *pThreadCount = count;
1993
1994 if (count == 0) {
1995 *ppThreadIds = NULL;
1996 } else {
1997 ObjectId* ptr;
1998 ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
1999
2000 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2001 Object* group;
2002
2003 /* Skip over the JDWP support thread. Some debuggers
2004 * get bent out of shape when they can't suspend and
2005 * query all threads, so it's easier if we just don't
2006 * tell them about us.
2007 */
2008 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2009 continue;
2010
2011 /* This thread is currently being created, and isn't ready
2012 * to be seen by the debugger yet.
2013 */
2014 if (thread->threadObj == NULL)
2015 continue;
2016
2017 group = dvmGetFieldObject(thread->threadObj,
2018 gDvm.offJavaLangThread_group);
2019 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2020 {
2021 *ptr++ = objectToObjectId(thread->threadObj);
2022 count--;
2023 }
2024 }
2025
2026 assert(count == 0);
2027 }
2028
2029 dvmUnlockThreadList();
2030 }
2031
2032 /*
2033 * Get all threads.
2034 *
2035 * The caller must free "*ppThreadIds".
2036 */
dvmDbgGetAllThreads(ObjectId ** ppThreadIds,u4 * pThreadCount)2037 void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
2038 {
2039 dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
2040 }
2041
2042
2043 /*
2044 * Count up the #of frames on the thread's stack.
2045 *
2046 * Returns -1 on failure.
2047 */
dvmDbgGetThreadFrameCount(ObjectId threadId)2048 int dvmDbgGetThreadFrameCount(ObjectId threadId)
2049 {
2050 Object* threadObj;
2051 Thread* thread;
2052 int count = -1;
2053
2054 threadObj = objectIdToObject(threadId);
2055
2056 dvmLockThreadList(NULL);
2057 thread = threadObjToThread(threadObj);
2058 if (thread != NULL) {
2059 count = dvmComputeExactFrameDepth(thread->interpSave.curFrame);
2060 }
2061 dvmUnlockThreadList();
2062
2063 return count;
2064 }
2065
2066 /*
2067 * Get info for frame N from the specified thread's stack.
2068 */
dvmDbgGetThreadFrame(ObjectId threadId,int num,FrameId * pFrameId,JdwpLocation * pLoc)2069 bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
2070 JdwpLocation* pLoc)
2071 {
2072 Object* threadObj;
2073 Thread* thread;
2074 void* framePtr;
2075 int count;
2076
2077 threadObj = objectIdToObject(threadId);
2078
2079 dvmLockThreadList(NULL);
2080
2081 thread = threadObjToThread(threadObj);
2082 if (thread == NULL)
2083 goto bail;
2084
2085 framePtr = thread->interpSave.curFrame;
2086 count = 0;
2087 while (framePtr != NULL) {
2088 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2089 const Method* method = saveArea->method;
2090
2091 if (!dvmIsBreakFrame((u4*)framePtr)) {
2092 if (count == num) {
2093 *pFrameId = frameToFrameId(framePtr);
2094 if (dvmIsInterfaceClass(method->clazz))
2095 pLoc->typeTag = TT_INTERFACE;
2096 else
2097 pLoc->typeTag = TT_CLASS;
2098 pLoc->classId = classObjectToRefTypeId(method->clazz);
2099 pLoc->methodId = methodToMethodId(method);
2100 if (dvmIsNativeMethod(method))
2101 pLoc->idx = (u8)-1;
2102 else
2103 pLoc->idx = saveArea->xtra.currentPc - method->insns;
2104 dvmUnlockThreadList();
2105 return true;
2106 }
2107
2108 count++;
2109 }
2110
2111 framePtr = saveArea->prevFrame;
2112 }
2113
2114 bail:
2115 dvmUnlockThreadList();
2116 return false;
2117 }
2118
2119 /*
2120 * Get the ThreadId for the current thread.
2121 */
dvmDbgGetThreadSelfId()2122 ObjectId dvmDbgGetThreadSelfId()
2123 {
2124 Thread* self = dvmThreadSelf();
2125 return objectToObjectId(self->threadObj);
2126 }
2127
2128 /*
2129 * Suspend the VM.
2130 */
dvmDbgSuspendVM(bool isEvent)2131 void dvmDbgSuspendVM(bool isEvent)
2132 {
2133 dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
2134 }
2135
2136 /*
2137 * Resume the VM.
2138 */
dvmDbgResumeVM()2139 void dvmDbgResumeVM()
2140 {
2141 dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
2142 }
2143
2144 /*
2145 * Suspend one thread (not ourselves).
2146 */
dvmDbgSuspendThread(ObjectId threadId)2147 void dvmDbgSuspendThread(ObjectId threadId)
2148 {
2149 Object* threadObj = objectIdToObject(threadId);
2150 Thread* thread;
2151
2152 dvmLockThreadList(NULL);
2153
2154 thread = threadObjToThread(threadObj);
2155 if (thread == NULL) {
2156 /* can happen if our ThreadDeath notify crosses in the mail */
2157 ALOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
2158 } else {
2159 dvmSuspendThread(thread);
2160 }
2161
2162 dvmUnlockThreadList();
2163 }
2164
2165 /*
2166 * Resume one thread (not ourselves).
2167 */
dvmDbgResumeThread(ObjectId threadId)2168 void dvmDbgResumeThread(ObjectId threadId)
2169 {
2170 Object* threadObj = objectIdToObject(threadId);
2171 Thread* thread;
2172
2173 dvmLockThreadList(NULL);
2174
2175 thread = threadObjToThread(threadObj);
2176 if (thread == NULL) {
2177 ALOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
2178 } else {
2179 dvmResumeThread(thread);
2180 }
2181
2182 dvmUnlockThreadList();
2183 }
2184
2185 /*
2186 * Suspend ourselves after sending an event to the debugger.
2187 */
dvmDbgSuspendSelf()2188 void dvmDbgSuspendSelf()
2189 {
2190 dvmSuspendSelf(true);
2191 }
2192
2193 /*
2194 * Get the "this" object for the specified frame.
2195 */
getThisObject(const u4 * framePtr)2196 static Object* getThisObject(const u4* framePtr)
2197 {
2198 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2199 const Method* method = saveArea->method;
2200 int argOffset = method->registersSize - method->insSize;
2201 Object* thisObj;
2202
2203 if (method == NULL) {
2204 /* this is a "break" frame? */
2205 assert(false);
2206 return NULL;
2207 }
2208
2209 LOGVV(" Pulling this object for frame at %p", framePtr);
2210 LOGVV(" Method='%s' native=%d static=%d this=%p",
2211 method->name, dvmIsNativeMethod(method),
2212 dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
2213
2214 /*
2215 * No "this" pointer for statics. No args on the interp stack for
2216 * native methods invoked directly from the VM.
2217 */
2218 if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
2219 thisObj = NULL;
2220 else
2221 thisObj = (Object*) framePtr[argOffset];
2222
2223 if (thisObj != NULL && !dvmIsHeapAddress(thisObj)) {
2224 ALOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL",
2225 framePtr, method->clazz->descriptor, method->name);
2226 thisObj = NULL;
2227 }
2228
2229 return thisObj;
2230 }
2231
2232 /*
2233 * Return the "this" object for the specified frame. The thread must be
2234 * suspended.
2235 */
dvmDbgGetThisObject(ObjectId threadId,FrameId frameId,ObjectId * pThisId)2236 bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
2237 {
2238 const u4* framePtr = frameIdToFrame(frameId);
2239 Object* thisObj;
2240
2241 UNUSED_PARAMETER(threadId);
2242
2243 thisObj = getThisObject(framePtr);
2244
2245 *pThisId = objectToObjectId(thisObj);
2246 return true;
2247 }
2248
2249 /*
2250 * Copy the value of a method argument or local variable into the
2251 * specified buffer. The value will be preceeded with the tag.
2252 *
2253 * The debugger includes the tags in the request. Object tags may
2254 * be updated with a more refined type.
2255 */
dvmDbgGetLocalValue(ObjectId threadId,FrameId frameId,int slot,u1 tag,u1 * buf,int expectedLen)2256 void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
2257 u1 tag, u1* buf, int expectedLen)
2258 {
2259 const u4* framePtr = frameIdToFrame(frameId);
2260 Object* objVal;
2261 u4 intVal;
2262 u8 longVal;
2263
2264 UNUSED_PARAMETER(threadId);
2265
2266 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2267
2268 switch (tag) {
2269 case JT_BOOLEAN:
2270 assert(expectedLen == 1);
2271 intVal = framePtr[slot];
2272 set1(buf+1, intVal != 0);
2273 break;
2274 case JT_BYTE:
2275 assert(expectedLen == 1);
2276 intVal = framePtr[slot];
2277 set1(buf+1, intVal);
2278 break;
2279 case JT_SHORT:
2280 case JT_CHAR:
2281 assert(expectedLen == 2);
2282 intVal = framePtr[slot];
2283 set2BE(buf+1, intVal);
2284 break;
2285 case JT_INT:
2286 case JT_FLOAT:
2287 assert(expectedLen == 4);
2288 intVal = framePtr[slot];
2289 set4BE(buf+1, intVal);
2290 break;
2291 case JT_ARRAY:
2292 assert(expectedLen == sizeof(ObjectId));
2293 {
2294 /* convert to "ObjectId" */
2295 objVal = (Object*)framePtr[slot];
2296 if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
2297 ALOGW("JDWP: slot %d expected to hold array, %p invalid",
2298 slot, objVal);
2299 dvmAbort(); // DEBUG: make it obvious
2300 objVal = NULL;
2301 tag = JT_OBJECT; // JT_ARRAY not expected for NULL ref
2302 }
2303 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2304 }
2305 break;
2306 case JT_OBJECT:
2307 assert(expectedLen == sizeof(ObjectId));
2308 {
2309 /* convert to "ObjectId" */
2310 objVal = (Object*)framePtr[slot];
2311
2312 if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
2313 ALOGW("JDWP: slot %d expected to hold object, %p invalid",
2314 slot, objVal);
2315 dvmAbort(); // DEBUG: make it obvious
2316 objVal = NULL;
2317 }
2318 tag = tagFromObject(objVal);
2319 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2320 }
2321 break;
2322 case JT_DOUBLE:
2323 case JT_LONG:
2324 assert(expectedLen == 8);
2325 memcpy(&longVal, &framePtr[slot], 8);
2326 set8BE(buf+1, longVal);
2327 break;
2328 default:
2329 ALOGE("ERROR: unhandled tag '%c'", tag);
2330 assert(false);
2331 break;
2332 }
2333
2334 /* prepend tag, which may have been updated */
2335 set1(buf, tag);
2336 }
2337
2338 /*
2339 * Copy a new value into an argument or local variable.
2340 */
dvmDbgSetLocalValue(ObjectId threadId,FrameId frameId,int slot,u1 tag,u8 value,int width)2341 void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
2342 u8 value, int width)
2343 {
2344 u4* framePtr = frameIdToFrame(frameId);
2345
2346 UNUSED_PARAMETER(threadId);
2347
2348 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2349
2350 switch (tag) {
2351 case JT_BOOLEAN:
2352 assert(width == 1);
2353 framePtr[slot] = (u4)value;
2354 break;
2355 case JT_BYTE:
2356 assert(width == 1);
2357 framePtr[slot] = (u4)value;
2358 break;
2359 case JT_SHORT:
2360 case JT_CHAR:
2361 assert(width == 2);
2362 framePtr[slot] = (u4)value;
2363 break;
2364 case JT_INT:
2365 case JT_FLOAT:
2366 assert(width == 4);
2367 framePtr[slot] = (u4)value;
2368 break;
2369 case JT_STRING:
2370 /* The debugger calls VirtualMachine.CreateString to create a new
2371 * string, then uses this to set the object reference, when you
2372 * edit a String object */
2373 case JT_ARRAY:
2374 case JT_OBJECT:
2375 assert(width == sizeof(ObjectId));
2376 framePtr[slot] = (u4) objectIdToObject(value);
2377 break;
2378 case JT_DOUBLE:
2379 case JT_LONG:
2380 assert(width == 8);
2381 memcpy(&framePtr[slot], &value, 8);
2382 break;
2383 case JT_VOID:
2384 case JT_CLASS_OBJECT:
2385 case JT_THREAD:
2386 case JT_THREAD_GROUP:
2387 case JT_CLASS_LOADER:
2388 /* not expecting these from debugger; fall through to failure */
2389 default:
2390 ALOGE("ERROR: unhandled tag '%c'", tag);
2391 assert(false);
2392 break;
2393 }
2394 }
2395
2396
2397 /*
2398 * ===========================================================================
2399 * Debugger notification
2400 * ===========================================================================
2401 */
2402
2403 /*
2404 * Tell JDWP that a breakpoint address has been reached.
2405 *
2406 * "pcOffset" will be -1 for native methods.
2407 * "thisPtr" will be NULL for static methods.
2408 */
dvmDbgPostLocationEvent(const Method * method,int pcOffset,Object * thisPtr,int eventFlags)2409 void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
2410 Object* thisPtr, int eventFlags)
2411 {
2412 JdwpLocation loc;
2413
2414 if (dvmIsInterfaceClass(method->clazz))
2415 loc.typeTag = TT_INTERFACE;
2416 else
2417 loc.typeTag = TT_CLASS;
2418 loc.classId = classObjectToRefTypeId(method->clazz);
2419 loc.methodId = methodToMethodId(method);
2420 loc.idx = pcOffset;
2421
2422 /*
2423 * Note we use "NoReg" so we don't keep track of references that are
2424 * never actually sent to the debugger. The "thisPtr" is only used to
2425 * compare against registered events.
2426 */
2427
2428 if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
2429 objectToObjectIdNoReg(thisPtr), eventFlags))
2430 {
2431 classObjectToRefTypeId(method->clazz);
2432 objectToObjectId(thisPtr);
2433 }
2434 }
2435
2436 /*
2437 * Tell JDWP that an exception has occurred.
2438 */
dvmDbgPostException(void * throwFp,int throwRelPc,void * catchFp,int catchRelPc,Object * exception)2439 void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
2440 int catchRelPc, Object* exception)
2441 {
2442 JdwpLocation throwLoc, catchLoc;
2443 const Method* throwMeth;
2444 const Method* catchMeth;
2445
2446 throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
2447 if (dvmIsInterfaceClass(throwMeth->clazz))
2448 throwLoc.typeTag = TT_INTERFACE;
2449 else
2450 throwLoc.typeTag = TT_CLASS;
2451 throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
2452 throwLoc.methodId = methodToMethodId(throwMeth);
2453 throwLoc.idx = throwRelPc;
2454
2455 if (catchRelPc < 0) {
2456 memset(&catchLoc, 0, sizeof(catchLoc));
2457 } else {
2458 catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
2459 if (dvmIsInterfaceClass(catchMeth->clazz))
2460 catchLoc.typeTag = TT_INTERFACE;
2461 else
2462 catchLoc.typeTag = TT_CLASS;
2463 catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
2464 catchLoc.methodId = methodToMethodId(catchMeth);
2465 catchLoc.idx = catchRelPc;
2466 }
2467
2468 /* need this for InstanceOnly filters */
2469 Object* thisObj = getThisObject((u4*)throwFp);
2470
2471 /*
2472 * Hand the event to the JDWP exception handler. Note we're using the
2473 * "NoReg" objectID on the exception, which is not strictly correct --
2474 * the exception object WILL be passed up to the debugger if the
2475 * debugger is interested in the event. We do this because the current
2476 * implementation of the debugger object registry never throws anything
2477 * away, and some people were experiencing a fatal build up of exception
2478 * objects when dealing with certain libraries.
2479 */
2480 dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
2481 objectToObjectIdNoReg(exception),
2482 classObjectToRefTypeId(exception->clazz), &catchLoc,
2483 objectToObjectId(thisObj));
2484 }
2485
2486 /*
2487 * Tell JDWP and/or DDMS that a thread has started.
2488 */
dvmDbgPostThreadStart(Thread * thread)2489 void dvmDbgPostThreadStart(Thread* thread)
2490 {
2491 if (gDvm.debuggerActive) {
2492 dvmJdwpPostThreadChange(gDvm.jdwpState,
2493 objectToObjectId(thread->threadObj), true);
2494 }
2495 if (gDvm.ddmThreadNotification)
2496 dvmDdmSendThreadNotification(thread, true);
2497 }
2498
2499 /*
2500 * Tell JDWP and/or DDMS that a thread has gone away.
2501 */
dvmDbgPostThreadDeath(Thread * thread)2502 void dvmDbgPostThreadDeath(Thread* thread)
2503 {
2504 if (gDvm.debuggerActive) {
2505 dvmJdwpPostThreadChange(gDvm.jdwpState,
2506 objectToObjectId(thread->threadObj), false);
2507 }
2508 if (gDvm.ddmThreadNotification)
2509 dvmDdmSendThreadNotification(thread, false);
2510 }
2511
2512 /*
2513 * Tell JDWP that a new class has been prepared.
2514 */
dvmDbgPostClassPrepare(ClassObject * clazz)2515 void dvmDbgPostClassPrepare(ClassObject* clazz)
2516 {
2517 const char* signature;
2518 int tag;
2519
2520 if (dvmIsInterfaceClass(clazz))
2521 tag = TT_INTERFACE;
2522 else
2523 tag = TT_CLASS;
2524
2525 // TODO - we currently always send both "verified" and "prepared" since
2526 // debuggers seem to like that. There might be some advantage to honesty,
2527 // since the class may not yet be verified.
2528 signature = jniSignature(clazz);
2529 dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
2530 signature, CS_VERIFIED | CS_PREPARED);
2531 }
2532
2533 /*
2534 * The JDWP event mechanism has registered an event with a LocationOnly
2535 * mod. Tell the interpreter to call us if we hit the specified
2536 * address.
2537 */
dvmDbgWatchLocation(const JdwpLocation * pLoc)2538 bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
2539 {
2540 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2541 assert(!dvmIsNativeMethod(method));
2542 dvmAddBreakAddr(method, pLoc->idx);
2543 return true; /* assume success */
2544 }
2545
2546 /*
2547 * An event with a LocationOnly mod has been removed.
2548 */
dvmDbgUnwatchLocation(const JdwpLocation * pLoc)2549 void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
2550 {
2551 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2552 assert(!dvmIsNativeMethod(method));
2553 dvmClearBreakAddr(method, pLoc->idx);
2554 }
2555
2556 /*
2557 * The JDWP event mechanism has registered a single-step event. Tell
2558 * the interpreter about it.
2559 */
dvmDbgConfigureStep(ObjectId threadId,JdwpStepSize size,JdwpStepDepth depth)2560 bool dvmDbgConfigureStep(ObjectId threadId, JdwpStepSize size,
2561 JdwpStepDepth depth)
2562 {
2563 Object* threadObj;
2564 Thread* thread;
2565 bool result = false;
2566
2567 threadObj = objectIdToObject(threadId);
2568 assert(threadObj != NULL);
2569
2570 /*
2571 * Get a pointer to the Thread struct for this ID. The pointer will
2572 * be used strictly for comparisons against the current thread pointer
2573 * after the setup is complete, so we can safely release the lock.
2574 */
2575 dvmLockThreadList(NULL);
2576 thread = threadObjToThread(threadObj);
2577
2578 if (thread == NULL) {
2579 ALOGE("Thread for single-step not found");
2580 goto bail;
2581 }
2582 if (!dvmIsSuspended(thread)) {
2583 ALOGE("Thread for single-step not suspended");
2584 assert(!"non-susp step"); // I want to know if this can happen
2585 goto bail;
2586 }
2587
2588 assert(dvmIsSuspended(thread));
2589 if (!dvmAddSingleStep(thread, size, depth))
2590 goto bail;
2591
2592 result = true;
2593
2594 bail:
2595 dvmUnlockThreadList();
2596 return result;
2597 }
2598
2599 /*
2600 * A single-step event has been removed.
2601 */
dvmDbgUnconfigureStep(ObjectId threadId)2602 void dvmDbgUnconfigureStep(ObjectId threadId)
2603 {
2604 UNUSED_PARAMETER(threadId);
2605
2606 /* right now it's global, so don't need to find Thread */
2607 dvmClearSingleStep(NULL);
2608 }
2609
2610 /*
2611 * Invoke a method in a thread that has been stopped on a breakpoint or
2612 * other debugger event. (This function is called from the JDWP thread.)
2613 *
2614 * Note that access control is not enforced, per spec.
2615 */
dvmDbgInvokeMethod(ObjectId threadId,ObjectId objectId,RefTypeId classId,MethodId methodId,u4 numArgs,ObjectId * argArray,u4 options,u1 * pResultTag,u8 * pResultValue,ObjectId * pExceptObj)2616 JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
2617 RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
2618 u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
2619 {
2620 Object* threadObj = objectIdToObject(threadId);
2621
2622 dvmLockThreadList(NULL);
2623
2624 Thread* targetThread = threadObjToThread(threadObj);
2625 if (targetThread == NULL) {
2626 dvmUnlockThreadList();
2627 return ERR_INVALID_THREAD; /* thread does not exist */
2628 }
2629 if (!targetThread->invokeReq.ready) {
2630 dvmUnlockThreadList();
2631 return ERR_INVALID_THREAD; /* thread not stopped by event */
2632 }
2633
2634 /*
2635 * We currently have a bug where we don't successfully resume the
2636 * target thread if the suspend count is too deep. We're expected to
2637 * require one "resume" for each "suspend", but when asked to execute
2638 * a method we have to resume fully and then re-suspend it back to the
2639 * same level. (The easiest way to cause this is to type "suspend"
2640 * multiple times in jdb.)
2641 *
2642 * It's unclear what this means when the event specifies "resume all"
2643 * and some threads are suspended more deeply than others. This is
2644 * a rare problem, so for now we just prevent it from hanging forever
2645 * by rejecting the method invocation request. Without this, we will
2646 * be stuck waiting on a suspended thread.
2647 */
2648 if (targetThread->suspendCount > 1) {
2649 ALOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
2650 "for method exec",
2651 dvmThreadSelf()->threadId, targetThread->threadId,
2652 targetThread->suspendCount);
2653 dvmUnlockThreadList();
2654 return ERR_THREAD_SUSPENDED; /* probably not expected here */
2655 }
2656
2657 /*
2658 * TODO: ought to screen the various IDs, and verify that the argument
2659 * list is valid.
2660 */
2661
2662 targetThread->invokeReq.obj = objectIdToObject(objectId);
2663 targetThread->invokeReq.thread = threadObj;
2664 targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
2665 targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
2666 targetThread->invokeReq.numArgs = numArgs;
2667 targetThread->invokeReq.argArray = argArray;
2668 targetThread->invokeReq.options = options;
2669 targetThread->invokeReq.invokeNeeded = true;
2670
2671 /*
2672 * This is a bit risky -- if the thread goes away we're sitting high
2673 * and dry -- but we must release this before the dvmResumeAllThreads
2674 * call, and it's unwise to hold it during dvmWaitForSuspend.
2675 */
2676 dvmUnlockThreadList();
2677
2678 /*
2679 * We change our (JDWP thread) status, which should be THREAD_RUNNING,
2680 * so the VM can suspend for a GC if the invoke request causes us to
2681 * run out of memory. It's also a good idea to change it before locking
2682 * the invokeReq mutex, although that should never be held for long.
2683 */
2684 Thread* self = dvmThreadSelf();
2685 ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
2686
2687 ALOGV(" Transferring control to event thread");
2688 dvmLockMutex(&targetThread->invokeReq.lock);
2689
2690 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2691 ALOGV(" Resuming all threads");
2692 dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2693 } else {
2694 ALOGV(" Resuming event thread only");
2695 dvmResumeThread(targetThread);
2696 }
2697
2698 /*
2699 * Wait for the request to finish executing.
2700 */
2701 while (targetThread->invokeReq.invokeNeeded) {
2702 pthread_cond_wait(&targetThread->invokeReq.cv,
2703 &targetThread->invokeReq.lock);
2704 }
2705 dvmUnlockMutex(&targetThread->invokeReq.lock);
2706 ALOGV(" Control has returned from event thread");
2707
2708 /* wait for thread to re-suspend itself */
2709 dvmWaitForSuspend(targetThread);
2710
2711 /*
2712 * Done waiting, switch back to RUNNING.
2713 */
2714 dvmChangeStatus(self, oldStatus);
2715
2716 /*
2717 * Suspend the threads. We waited for the target thread to suspend
2718 * itself, so all we need to do is suspend the others.
2719 *
2720 * The suspendAllThreads() call will double-suspend the event thread,
2721 * so we want to resume the target thread once to keep the books straight.
2722 */
2723 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2724 ALOGV(" Suspending all threads");
2725 dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2726 ALOGV(" Resuming event thread to balance the count");
2727 dvmResumeThread(targetThread);
2728 }
2729
2730 /*
2731 * Set up the result.
2732 */
2733 *pResultTag = targetThread->invokeReq.resultTag;
2734 if (isTagPrimitive(targetThread->invokeReq.resultTag))
2735 *pResultValue = targetThread->invokeReq.resultValue.j;
2736 else {
2737 Object* tmpObj = (Object*)targetThread->invokeReq.resultValue.l;
2738 *pResultValue = objectToObjectId(tmpObj);
2739 }
2740 *pExceptObj = targetThread->invokeReq.exceptObj;
2741 return targetThread->invokeReq.err;
2742 }
2743
2744 /*
2745 * Return a basic tag value for the return type.
2746 */
getReturnTypeBasicTag(const Method * method)2747 static u1 getReturnTypeBasicTag(const Method* method)
2748 {
2749 const char* descriptor = dexProtoGetReturnType(&method->prototype);
2750 return basicTagFromDescriptor(descriptor);
2751 }
2752
2753 /*
2754 * Execute the method described by "*pReq".
2755 *
2756 * We're currently in VMWAIT, because we're stopped on a breakpoint. We
2757 * want to switch to RUNNING while we execute.
2758 */
dvmDbgExecuteMethod(DebugInvokeReq * pReq)2759 void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
2760 {
2761 Thread* self = dvmThreadSelf();
2762 const Method* meth;
2763 Object* oldExcept;
2764 ThreadStatus oldStatus;
2765
2766 /*
2767 * We can be called while an exception is pending in the VM. We need
2768 * to preserve that across the method invocation.
2769 */
2770 oldExcept = dvmGetException(self);
2771 if (oldExcept != NULL) {
2772 dvmAddTrackedAlloc(oldExcept, self);
2773 dvmClearException(self);
2774 }
2775
2776 oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
2777
2778 /*
2779 * Translate the method through the vtable, unless we're calling a
2780 * direct method or the debugger wants to suppress it.
2781 */
2782 if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
2783 dvmIsDirectMethod(pReq->method))
2784 {
2785 meth = pReq->method;
2786 } else {
2787 meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
2788 }
2789 assert(meth != NULL);
2790
2791 assert(sizeof(jvalue) == sizeof(u8));
2792
2793 IF_ALOGV() {
2794 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2795 ALOGV("JDWP invoking method %p/%p %s.%s:%s",
2796 pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
2797 free(desc);
2798 }
2799
2800 dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
2801 (jvalue*)pReq->argArray);
2802 pReq->exceptObj = objectToObjectId(dvmGetException(self));
2803 pReq->resultTag = getReturnTypeBasicTag(meth);
2804 if (pReq->exceptObj != 0) {
2805 Object* exc = dvmGetException(self);
2806 ALOGD(" JDWP invocation returning with exceptObj=%p (%s)",
2807 exc, exc->clazz->descriptor);
2808 //dvmLogExceptionStackTrace();
2809 dvmClearException(self);
2810 /*
2811 * Nothing should try to use this, but it looks like something is.
2812 * Make it null to be safe.
2813 */
2814 pReq->resultValue.j = 0; /*0xadadadad;*/
2815 } else if (pReq->resultTag == JT_OBJECT) {
2816 /* if no exception thrown, examine object result more closely */
2817 u1 newTag = tagFromObject((Object*)pReq->resultValue.l);
2818 if (newTag != pReq->resultTag) {
2819 LOGVV(" JDWP promoted result from %d to %d",
2820 pReq->resultTag, newTag);
2821 pReq->resultTag = newTag;
2822 }
2823
2824 /*
2825 * Register the object. We don't actually need an ObjectId yet,
2826 * but we do need to be sure that the GC won't move or discard the
2827 * object when we switch out of RUNNING. The ObjectId conversion
2828 * will add the object to the "do not touch" list.
2829 *
2830 * We can't use the "tracked allocation" mechanism here because
2831 * the object is going to be handed off to a different thread.
2832 */
2833 objectToObjectId((Object*)pReq->resultValue.l);
2834 }
2835
2836 if (oldExcept != NULL) {
2837 dvmSetException(self, oldExcept);
2838 dvmReleaseTrackedAlloc(oldExcept, self);
2839 }
2840 dvmChangeStatus(self, oldStatus);
2841 }
2842
2843 // for dvmAddressSetForLine
2844 struct AddressSetContext {
2845 bool lastAddressValid;
2846 u4 lastAddress;
2847 u4 lineNum;
2848 AddressSet *pSet;
2849 };
2850
2851 // for dvmAddressSetForLine
addressSetCb(void * cnxt,u4 address,u4 lineNum)2852 static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
2853 {
2854 AddressSetContext *pContext = (AddressSetContext *)cnxt;
2855
2856 if (lineNum == pContext->lineNum) {
2857 if (!pContext->lastAddressValid) {
2858 // Everything from this address until the next line change is ours
2859 pContext->lastAddress = address;
2860 pContext->lastAddressValid = true;
2861 }
2862 // else, If we're already in a valid range for this lineNum,
2863 // just keep going (shouldn't really happen)
2864 } else if (pContext->lastAddressValid) { // and the line number is new
2865 u4 i;
2866 // Add everything from the last entry up until here to the set
2867 for (i = pContext->lastAddress; i < address; i++) {
2868 dvmAddressSetSet(pContext->pSet, i);
2869 }
2870
2871 pContext->lastAddressValid = false;
2872 }
2873
2874 // there may be multiple entries for a line
2875 return 0;
2876 }
2877 /*
2878 * Build up a set of bytecode addresses associated with a line number
2879 */
dvmAddressSetForLine(const Method * method,int line)2880 const AddressSet *dvmAddressSetForLine(const Method* method, int line)
2881 {
2882 AddressSet *result;
2883 const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
2884 u4 insnsSize = dvmGetMethodInsnsSize(method);
2885 AddressSetContext context;
2886
2887 result = (AddressSet*)calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
2888 result->setSize = insnsSize;
2889
2890 memset(&context, 0, sizeof(context));
2891 context.pSet = result;
2892 context.lineNum = line;
2893 context.lastAddressValid = false;
2894
2895 dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
2896 method->clazz->descriptor,
2897 method->prototype.protoIdx,
2898 method->accessFlags,
2899 addressSetCb, NULL, &context);
2900
2901 // If the line number was the last in the position table...
2902 if (context.lastAddressValid) {
2903 u4 i;
2904 for (i = context.lastAddress; i < insnsSize; i++) {
2905 dvmAddressSetSet(result, i);
2906 }
2907 }
2908
2909 return result;
2910 }
2911
2912
2913 /*
2914 * ===========================================================================
2915 * Dalvik Debug Monitor support
2916 * ===========================================================================
2917 */
2918
2919 /*
2920 * We have received a DDM packet over JDWP. Hand it off to the VM.
2921 */
dvmDbgDdmHandlePacket(const u1 * buf,int dataLen,u1 ** pReplyBuf,int * pReplyLen)2922 bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
2923 int* pReplyLen)
2924 {
2925 return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
2926 }
2927
2928 /*
2929 * First DDM packet has arrived over JDWP. Notify the press.
2930 */
dvmDbgDdmConnected()2931 void dvmDbgDdmConnected()
2932 {
2933 dvmDdmConnected();
2934 }
2935
2936 /*
2937 * JDWP connection has dropped.
2938 */
dvmDbgDdmDisconnected()2939 void dvmDbgDdmDisconnected()
2940 {
2941 dvmDdmDisconnected();
2942 }
2943
2944 /*
2945 * Send up a JDWP event packet with a DDM chunk in it.
2946 */
dvmDbgDdmSendChunk(int type,size_t len,const u1 * buf)2947 void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
2948 {
2949 assert(buf != NULL);
2950 struct iovec vec[1] = { {(void*)buf, len} };
2951 dvmDbgDdmSendChunkV(type, vec, 1);
2952 }
2953
2954 /*
2955 * Send up a JDWP event packet with a DDM chunk in it. The chunk is
2956 * concatenated from multiple source buffers.
2957 */
dvmDbgDdmSendChunkV(int type,const struct iovec * iov,int iovcnt)2958 void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
2959 {
2960 if (gDvm.jdwpState == NULL) {
2961 ALOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)",
2962 type);
2963 return;
2964 }
2965
2966 dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);
2967 }
2968