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 LOGI("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 LOGV("+++ 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 LOGV("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 LOGI("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 LOGD("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 LOGI("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 LOGW("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 LOGE("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 LOGE("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 LOGW("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 LOGV(" --> 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 LOGW("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 LOGV(" --> 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 LOGV(" --> 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, start, count;
1268
1269 clazz = refTypeIdToClassObject(refTypeId);
1270 assert(clazz != NULL);
1271
1272 if (clazz->super == NULL)
1273 start = 0;
1274 else
1275 start = clazz->super->iftableCount;
1276
1277 count = clazz->iftableCount - start;
1278 expandBufAdd4BE(pReply, count);
1279 for (i = start; i < clazz->iftableCount; i++) {
1280 ClassObject* iface = clazz->iftable[i].clazz;
1281 expandBufAddRefTypeId(pReply, classObjectToRefTypeId(iface));
1282 }
1283 }
1284
1285 struct DebugCallbackContext {
1286 int numItems;
1287 ExpandBuf* pReply;
1288 // used by locals table
1289 bool withGeneric;
1290 };
1291
lineTablePositionsCb(void * cnxt,u4 address,u4 lineNum)1292 static int lineTablePositionsCb(void *cnxt, u4 address, u4 lineNum)
1293 {
1294 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1295
1296 expandBufAdd8BE(pContext->pReply, address);
1297 expandBufAdd4BE(pContext->pReply, lineNum);
1298 pContext->numItems++;
1299
1300 return 0;
1301 }
1302
1303 /*
1304 * For Method.LineTable: output the line table.
1305 *
1306 * Note we operate in Dalvik's 16-bit units rather than bytes.
1307 */
dvmDbgOutputLineTable(RefTypeId refTypeId,MethodId methodId,ExpandBuf * pReply)1308 void dvmDbgOutputLineTable(RefTypeId refTypeId, MethodId methodId,
1309 ExpandBuf* pReply)
1310 {
1311 Method* method;
1312 u8 start, end;
1313 DebugCallbackContext context;
1314
1315 memset (&context, 0, sizeof(DebugCallbackContext));
1316
1317 method = methodIdToMethod(refTypeId, methodId);
1318 if (dvmIsNativeMethod(method)) {
1319 start = (u8) -1;
1320 end = (u8) -1;
1321 } else {
1322 start = 0;
1323 end = dvmGetMethodInsnsSize(method);
1324 }
1325
1326 expandBufAdd8BE(pReply, start);
1327 expandBufAdd8BE(pReply, end);
1328
1329 // Add numLines later
1330 size_t numLinesOffset = expandBufGetLength(pReply);
1331 expandBufAdd4BE(pReply, 0);
1332
1333 context.pReply = pReply;
1334
1335 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1336 dvmGetMethodCode(method),
1337 method->clazz->descriptor,
1338 method->prototype.protoIdx,
1339 method->accessFlags,
1340 lineTablePositionsCb, NULL, &context);
1341
1342 set4BE(expandBufGetBuffer(pReply) + numLinesOffset, context.numItems);
1343 }
1344
1345 /*
1346 * Eclipse appears to expect that the "this" reference is in slot zero.
1347 * If it's not, the "variables" display will show two copies of "this",
1348 * possibly because it gets "this" from SF.ThisObject and then displays
1349 * all locals with nonzero slot numbers.
1350 *
1351 * So, we remap the item in slot 0 to 1000, and remap "this" to zero. On
1352 * SF.GetValues / SF.SetValues we map them back.
1353 */
tweakSlot(int slot,const char * name)1354 static int tweakSlot(int slot, const char* name)
1355 {
1356 int newSlot = slot;
1357
1358 if (strcmp(name, "this") == 0) // only remap "this" ptr
1359 newSlot = 0;
1360 else if (slot == 0) // always remap slot 0
1361 newSlot = kSlot0Sub;
1362
1363 LOGV("untweak: %d to %d", slot, newSlot);
1364 return newSlot;
1365 }
1366
1367 /*
1368 * Reverse Eclipse hack.
1369 */
untweakSlot(int slot,const void * framePtr)1370 static int untweakSlot(int slot, const void* framePtr)
1371 {
1372 int newSlot = slot;
1373
1374 if (slot == kSlot0Sub) {
1375 newSlot = 0;
1376 } else if (slot == 0) {
1377 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
1378 const Method* method = saveArea->method;
1379 newSlot = method->registersSize - method->insSize;
1380 }
1381
1382 LOGV("untweak: %d to %d", slot, newSlot);
1383 return newSlot;
1384 }
1385
variableTableCb(void * cnxt,u2 reg,u4 startAddress,u4 endAddress,const char * name,const char * descriptor,const char * signature)1386 static void variableTableCb (void *cnxt, u2 reg, u4 startAddress,
1387 u4 endAddress, const char *name, const char *descriptor,
1388 const char *signature)
1389 {
1390 DebugCallbackContext *pContext = (DebugCallbackContext *)cnxt;
1391
1392 reg = (u2) tweakSlot(reg, name);
1393
1394 LOGV(" %2d: %d(%d) '%s' '%s' slot=%d",
1395 pContext->numItems, startAddress, endAddress - startAddress,
1396 name, descriptor, reg);
1397
1398 expandBufAdd8BE(pContext->pReply, startAddress);
1399 expandBufAddUtf8String(pContext->pReply, (const u1*)name);
1400 expandBufAddUtf8String(pContext->pReply, (const u1*)descriptor);
1401 if (pContext->withGeneric) {
1402 expandBufAddUtf8String(pContext->pReply, (const u1*) signature);
1403 }
1404 expandBufAdd4BE(pContext->pReply, endAddress - startAddress);
1405 expandBufAdd4BE(pContext->pReply, reg);
1406
1407 pContext->numItems++;
1408 }
1409
1410 /*
1411 * For Method.VariableTable[WithGeneric]: output information about local
1412 * variables for the specified method.
1413 */
dvmDbgOutputVariableTable(RefTypeId refTypeId,MethodId methodId,bool withGeneric,ExpandBuf * pReply)1414 void dvmDbgOutputVariableTable(RefTypeId refTypeId, MethodId methodId,
1415 bool withGeneric, ExpandBuf* pReply)
1416 {
1417 Method* method;
1418 DebugCallbackContext context;
1419
1420 memset (&context, 0, sizeof(DebugCallbackContext));
1421
1422 method = methodIdToMethod(refTypeId, methodId);
1423
1424 expandBufAdd4BE(pReply, method->insSize);
1425
1426 // Add numLocals later
1427 size_t numLocalsOffset = expandBufGetLength(pReply);
1428 expandBufAdd4BE(pReply, 0);
1429
1430 context.pReply = pReply;
1431 context.withGeneric = withGeneric;
1432 dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile,
1433 dvmGetMethodCode(method),
1434 method->clazz->descriptor,
1435 method->prototype.protoIdx,
1436 method->accessFlags,
1437 NULL, variableTableCb, &context);
1438
1439 set4BE(expandBufGetBuffer(pReply) + numLocalsOffset, context.numItems);
1440 }
1441
1442 /*
1443 * Get the basic tag for an instance field.
1444 */
dvmDbgGetFieldBasicTag(ObjectId objId,FieldId fieldId)1445 u1 dvmDbgGetFieldBasicTag(ObjectId objId, FieldId fieldId)
1446 {
1447 Object* obj = objectIdToObject(objId);
1448 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1449 const Field* field = fieldIdToField(classId, fieldId);
1450 return basicTagFromDescriptor(field->signature);
1451 }
1452
1453 /*
1454 * Get the basic tag for a static field.
1455 */
dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId,FieldId fieldId)1456 u1 dvmDbgGetStaticFieldBasicTag(RefTypeId refTypeId, FieldId fieldId)
1457 {
1458 const Field* field = fieldIdToField(refTypeId, fieldId);
1459 return basicTagFromDescriptor(field->signature);
1460 }
1461
1462
1463 /*
1464 * Copy the value of a static field into the output buffer, preceded
1465 * by an appropriate tag. The tag is based on the value held by the
1466 * field, not the field's type.
1467 */
dvmDbgGetFieldValue(ObjectId objectId,FieldId fieldId,ExpandBuf * pReply)1468 void dvmDbgGetFieldValue(ObjectId objectId, FieldId fieldId, ExpandBuf* pReply)
1469 {
1470 Object* obj = objectIdToObject(objectId);
1471 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1472 InstField* ifield = (InstField*) fieldIdToField(classId, fieldId);
1473 u1 tag = basicTagFromDescriptor(ifield->signature);
1474
1475 if (tag == JT_ARRAY || tag == JT_OBJECT) {
1476 Object* objVal = dvmGetFieldObject(obj, ifield->byteOffset);
1477 tag = tagFromObject(objVal);
1478 expandBufAdd1(pReply, tag);
1479 expandBufAddObjectId(pReply, objectToObjectId(objVal));
1480 LOGV(" --> ifieldId %x --> tag '%c' %p", fieldId, tag, objVal);
1481 } else {
1482 LOGV(" --> ifieldId %x --> tag '%c'", fieldId, tag);
1483 expandBufAdd1(pReply, tag);
1484
1485 switch (tag) {
1486 case JT_BOOLEAN:
1487 expandBufAdd1(pReply, dvmGetFieldBoolean(obj, ifield->byteOffset));
1488 break;
1489 case JT_BYTE:
1490 expandBufAdd1(pReply, dvmGetFieldByte(obj, ifield->byteOffset));
1491 break;
1492 case JT_SHORT:
1493 expandBufAdd2BE(pReply, dvmGetFieldShort(obj, ifield->byteOffset));
1494 break;
1495 case JT_CHAR:
1496 expandBufAdd2BE(pReply, dvmGetFieldChar(obj, ifield->byteOffset));
1497 break;
1498 case JT_INT:
1499 case JT_FLOAT:
1500 expandBufAdd4BE(pReply, dvmGetFieldInt(obj, ifield->byteOffset));
1501 break;
1502 case JT_LONG:
1503 case JT_DOUBLE:
1504 expandBufAdd8BE(pReply, dvmGetFieldLong(obj, ifield->byteOffset));
1505 break;
1506 default:
1507 LOGE("ERROR: unhandled field type '%s'", ifield->signature);
1508 assert(false);
1509 break;
1510 }
1511 }
1512 }
1513
1514 /*
1515 * Set the value of the specified field.
1516 */
dvmDbgSetFieldValue(ObjectId objectId,FieldId fieldId,u8 value,int width)1517 void dvmDbgSetFieldValue(ObjectId objectId, FieldId fieldId, u8 value,
1518 int width)
1519 {
1520 Object* obj = objectIdToObject(objectId);
1521 RefTypeId classId = classObjectToRefTypeId(obj->clazz);
1522 InstField* field = (InstField*) fieldIdToField(classId, fieldId);
1523
1524 switch (field->signature[0]) {
1525 case JT_BOOLEAN:
1526 assert(width == 1);
1527 dvmSetFieldBoolean(obj, field->byteOffset, value != 0);
1528 break;
1529 case JT_BYTE:
1530 assert(width == 1);
1531 dvmSetFieldInt(obj, field->byteOffset, value);
1532 break;
1533 case JT_SHORT:
1534 case JT_CHAR:
1535 assert(width == 2);
1536 dvmSetFieldInt(obj, field->byteOffset, value);
1537 break;
1538 case JT_INT:
1539 case JT_FLOAT:
1540 assert(width == 4);
1541 dvmSetFieldInt(obj, field->byteOffset, value);
1542 break;
1543 case JT_ARRAY:
1544 case JT_OBJECT:
1545 assert(width == sizeof(ObjectId));
1546 dvmSetFieldObject(obj, field->byteOffset, objectIdToObject(value));
1547 break;
1548 case JT_DOUBLE:
1549 case JT_LONG:
1550 assert(width == 8);
1551 dvmSetFieldLong(obj, field->byteOffset, value);
1552 break;
1553 default:
1554 LOGE("ERROR: unhandled class type '%s'", field->signature);
1555 assert(false);
1556 break;
1557 }
1558 }
1559
1560 /*
1561 * Copy the value of a static field into the output buffer, preceded
1562 * by an appropriate tag. The tag is based on the value held by the
1563 * field, not the field's type.
1564 */
dvmDbgGetStaticFieldValue(RefTypeId refTypeId,FieldId fieldId,ExpandBuf * pReply)1565 void dvmDbgGetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1566 ExpandBuf* pReply)
1567 {
1568 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1569 u1 tag = basicTagFromDescriptor(sfield->signature);
1570
1571 if (tag == JT_ARRAY || tag == JT_OBJECT) {
1572 Object* objVal = dvmGetStaticFieldObject(sfield);
1573 tag = tagFromObject(objVal);
1574 expandBufAdd1(pReply, tag);
1575 expandBufAddObjectId(pReply, objectToObjectId(objVal));
1576 LOGV(" --> sfieldId %x --> tag '%c' %p", fieldId, tag, objVal);
1577 } else {
1578 JValue value;
1579
1580 LOGV(" --> sfieldId %x --> tag '%c'", fieldId, tag);
1581 expandBufAdd1(pReply, tag);
1582
1583 switch (tag) {
1584 case JT_BOOLEAN:
1585 expandBufAdd1(pReply, dvmGetStaticFieldBoolean(sfield));
1586 break;
1587 case JT_BYTE:
1588 expandBufAdd1(pReply, dvmGetStaticFieldByte(sfield));
1589 break;
1590 case JT_SHORT:
1591 expandBufAdd2BE(pReply, dvmGetStaticFieldShort(sfield));
1592 break;
1593 case JT_CHAR:
1594 expandBufAdd2BE(pReply, dvmGetStaticFieldChar(sfield));
1595 break;
1596 case JT_INT:
1597 expandBufAdd4BE(pReply, dvmGetStaticFieldInt(sfield));
1598 break;
1599 case JT_FLOAT:
1600 value.f = dvmGetStaticFieldFloat(sfield);
1601 expandBufAdd4BE(pReply, value.i);
1602 break;
1603 case JT_LONG:
1604 expandBufAdd8BE(pReply, dvmGetStaticFieldLong(sfield));
1605 break;
1606 case JT_DOUBLE:
1607 value.d = dvmGetStaticFieldDouble(sfield);
1608 expandBufAdd8BE(pReply, value.j);
1609 break;
1610 default:
1611 LOGE("ERROR: unhandled field type '%s'", sfield->signature);
1612 assert(false);
1613 break;
1614 }
1615 }
1616 }
1617
1618 /*
1619 * Set the value of a static field.
1620 */
dvmDbgSetStaticFieldValue(RefTypeId refTypeId,FieldId fieldId,u8 rawValue,int width)1621 void dvmDbgSetStaticFieldValue(RefTypeId refTypeId, FieldId fieldId,
1622 u8 rawValue, int width)
1623 {
1624 StaticField* sfield = (StaticField*) fieldIdToField(refTypeId, fieldId);
1625 Object* objVal;
1626 JValue value;
1627
1628 value.j = rawValue;
1629
1630 switch (sfield->signature[0]) {
1631 case JT_BOOLEAN:
1632 assert(width == 1);
1633 dvmSetStaticFieldBoolean(sfield, value.z);
1634 break;
1635 case JT_BYTE:
1636 assert(width == 1);
1637 dvmSetStaticFieldByte(sfield, value.b);
1638 break;
1639 case JT_SHORT:
1640 assert(width == 2);
1641 dvmSetStaticFieldShort(sfield, value.s);
1642 break;
1643 case JT_CHAR:
1644 assert(width == 2);
1645 dvmSetStaticFieldChar(sfield, value.c);
1646 break;
1647 case JT_INT:
1648 assert(width == 4);
1649 dvmSetStaticFieldInt(sfield, value.i);
1650 break;
1651 case JT_FLOAT:
1652 assert(width == 4);
1653 dvmSetStaticFieldFloat(sfield, value.f);
1654 break;
1655 case JT_ARRAY:
1656 case JT_OBJECT:
1657 assert(width == sizeof(ObjectId));
1658 objVal = objectIdToObject(rawValue);
1659 dvmSetStaticFieldObject(sfield, objVal);
1660 break;
1661 case JT_LONG:
1662 assert(width == 8);
1663 dvmSetStaticFieldLong(sfield, value.j);
1664 break;
1665 case JT_DOUBLE:
1666 assert(width == 8);
1667 dvmSetStaticFieldDouble(sfield, value.d);
1668 break;
1669 default:
1670 LOGE("ERROR: unhandled class type '%s'", sfield->signature);
1671 assert(false);
1672 break;
1673 }
1674 }
1675
1676 /*
1677 * Convert a string object to a UTF-8 string.
1678 *
1679 * Returns a newly-allocated string.
1680 */
dvmDbgStringToUtf8(ObjectId strId)1681 char* dvmDbgStringToUtf8(ObjectId strId)
1682 {
1683 StringObject* strObj = (StringObject*) objectIdToObject(strId);
1684
1685 return dvmCreateCstrFromString(strObj);
1686 }
1687
1688
1689 /*
1690 * ===========================================================================
1691 * Thread and ThreadGroup
1692 * ===========================================================================
1693 */
1694
1695 /*
1696 * Convert a thread object to a Thread ptr.
1697 *
1698 * This currently requires running through the list of threads and finding
1699 * a match.
1700 *
1701 * IMPORTANT: grab gDvm.threadListLock before calling here.
1702 */
threadObjToThread(Object * threadObj)1703 static Thread* threadObjToThread(Object* threadObj)
1704 {
1705 Thread* thread;
1706
1707 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1708 if (thread->threadObj == threadObj)
1709 break;
1710 }
1711
1712 return thread;
1713 }
1714
1715 /*
1716 * Get the status and suspend state of a thread.
1717 */
dvmDbgGetThreadStatus(ObjectId threadId,u4 * pThreadStatus,u4 * pSuspendStatus)1718 bool dvmDbgGetThreadStatus(ObjectId threadId, u4* pThreadStatus,
1719 u4* pSuspendStatus)
1720 {
1721 Object* threadObj;
1722 Thread* thread;
1723 bool result = false;
1724
1725 threadObj = objectIdToObject(threadId);
1726 assert(threadObj != NULL);
1727
1728 /* lock the thread list, so the thread doesn't vanish while we work */
1729 dvmLockThreadList(NULL);
1730
1731 thread = threadObjToThread(threadObj);
1732 if (thread == NULL)
1733 goto bail;
1734
1735 switch (thread->status) {
1736 case THREAD_ZOMBIE: *pThreadStatus = TS_ZOMBIE; break;
1737 case THREAD_RUNNING: *pThreadStatus = TS_RUNNING; break;
1738 case THREAD_TIMED_WAIT: *pThreadStatus = TS_SLEEPING; break;
1739 case THREAD_MONITOR: *pThreadStatus = TS_MONITOR; break;
1740 case THREAD_WAIT: *pThreadStatus = TS_WAIT; break;
1741 case THREAD_INITIALIZING: *pThreadStatus = TS_ZOMBIE; break;
1742 case THREAD_STARTING: *pThreadStatus = TS_ZOMBIE; break;
1743 case THREAD_NATIVE: *pThreadStatus = TS_RUNNING; break;
1744 case THREAD_VMWAIT: *pThreadStatus = TS_WAIT; break;
1745 case THREAD_SUSPENDED: *pThreadStatus = TS_RUNNING; break;
1746 default:
1747 assert(false);
1748 *pThreadStatus = THREAD_ZOMBIE;
1749 break;
1750 }
1751
1752 if (dvmIsSuspended(thread))
1753 *pSuspendStatus = SUSPEND_STATUS_SUSPENDED;
1754 else
1755 *pSuspendStatus = 0;
1756
1757 result = true;
1758
1759 bail:
1760 dvmUnlockThreadList();
1761 return result;
1762 }
1763
1764 /*
1765 * Get the thread's suspend count.
1766 */
dvmDbgGetThreadSuspendCount(ObjectId threadId)1767 u4 dvmDbgGetThreadSuspendCount(ObjectId threadId)
1768 {
1769 Object* threadObj;
1770 Thread* thread;
1771 u4 result = 0;
1772
1773 threadObj = objectIdToObject(threadId);
1774 assert(threadObj != NULL);
1775
1776 /* lock the thread list, so the thread doesn't vanish while we work */
1777 dvmLockThreadList(NULL);
1778
1779 thread = threadObjToThread(threadObj);
1780 if (thread == NULL)
1781 goto bail;
1782
1783 result = thread->suspendCount;
1784
1785 bail:
1786 dvmUnlockThreadList();
1787 return result;
1788 }
1789
1790 /*
1791 * Determine whether or not a thread exists in the VM's thread list.
1792 *
1793 * Returns "true" if the thread exists.
1794 */
dvmDbgThreadExists(ObjectId threadId)1795 bool dvmDbgThreadExists(ObjectId threadId)
1796 {
1797 Object* threadObj;
1798 Thread* thread;
1799 bool result;
1800
1801 threadObj = objectIdToObject(threadId);
1802 assert(threadObj != NULL);
1803
1804 /* lock the thread list, so the thread doesn't vanish while we work */
1805 dvmLockThreadList(NULL);
1806
1807 thread = threadObjToThread(threadObj);
1808 if (thread == NULL)
1809 result = false;
1810 else
1811 result = true;
1812
1813 dvmUnlockThreadList();
1814 return result;
1815 }
1816
1817 /*
1818 * Determine whether or not a thread is suspended.
1819 *
1820 * Returns "false" if the thread is running or doesn't exist.
1821 */
dvmDbgIsSuspended(ObjectId threadId)1822 bool dvmDbgIsSuspended(ObjectId threadId)
1823 {
1824 Object* threadObj;
1825 Thread* thread;
1826 bool result = false;
1827
1828 threadObj = objectIdToObject(threadId);
1829 assert(threadObj != NULL);
1830
1831 /* lock the thread list, so the thread doesn't vanish while we work */
1832 dvmLockThreadList(NULL);
1833
1834 thread = threadObjToThread(threadObj);
1835 if (thread == NULL)
1836 goto bail;
1837
1838 result = dvmIsSuspended(thread);
1839
1840 bail:
1841 dvmUnlockThreadList();
1842 return result;
1843 }
1844
1845 /*
1846 * Return the ObjectId for the "system" thread group.
1847 */
dvmDbgGetSystemThreadGroupId()1848 ObjectId dvmDbgGetSystemThreadGroupId()
1849 {
1850 Object* groupObj = dvmGetSystemThreadGroup();
1851 return objectToObjectId(groupObj);
1852 }
1853
1854 /*
1855 * Return the ObjectId for the "main" thread group.
1856 */
dvmDbgGetMainThreadGroupId()1857 ObjectId dvmDbgGetMainThreadGroupId()
1858 {
1859 Object* groupObj = dvmGetMainThreadGroup();
1860 return objectToObjectId(groupObj);
1861 }
1862
1863 /*
1864 * Get the name of a thread.
1865 *
1866 * Returns a newly-allocated string.
1867 */
dvmDbgGetThreadName(ObjectId threadId)1868 char* dvmDbgGetThreadName(ObjectId threadId)
1869 {
1870 Object* threadObj;
1871 StringObject* nameStr;
1872 char* str;
1873 char* result;
1874
1875 threadObj = objectIdToObject(threadId);
1876 assert(threadObj != NULL);
1877
1878 nameStr = (StringObject*) dvmGetFieldObject(threadObj,
1879 gDvm.offJavaLangThread_name);
1880 str = dvmCreateCstrFromString(nameStr);
1881 result = (char*) malloc(strlen(str) + 20);
1882
1883 /* lock the thread list, so the thread doesn't vanish while we work */
1884 dvmLockThreadList(NULL);
1885 Thread* thread = threadObjToThread(threadObj);
1886 if (thread != NULL)
1887 sprintf(result, "<%d> %s", thread->threadId, str);
1888 else
1889 sprintf(result, "%s", str);
1890 dvmUnlockThreadList();
1891
1892 free(str);
1893 return result;
1894 }
1895
1896 /*
1897 * Get a thread's group.
1898 */
dvmDbgGetThreadGroup(ObjectId threadId)1899 ObjectId dvmDbgGetThreadGroup(ObjectId threadId)
1900 {
1901 Object* threadObj;
1902 Object* group;
1903
1904 threadObj = objectIdToObject(threadId);
1905 assert(threadObj != NULL);
1906
1907 group = dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_group);
1908 return objectToObjectId(group);
1909 }
1910
1911
1912 /*
1913 * Get the name of a thread group.
1914 *
1915 * Returns a newly-allocated string.
1916 */
dvmDbgGetThreadGroupName(ObjectId threadGroupId)1917 char* dvmDbgGetThreadGroupName(ObjectId threadGroupId)
1918 {
1919 Object* threadGroup;
1920 StringObject* nameStr;
1921
1922 threadGroup = objectIdToObject(threadGroupId);
1923 assert(threadGroup != NULL);
1924
1925 nameStr = (StringObject*)
1926 dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_name);
1927 return dvmCreateCstrFromString(nameStr);
1928 }
1929
1930 /*
1931 * Get the parent of a thread group.
1932 *
1933 * Returns a newly-allocated string.
1934 */
dvmDbgGetThreadGroupParent(ObjectId threadGroupId)1935 ObjectId dvmDbgGetThreadGroupParent(ObjectId threadGroupId)
1936 {
1937 Object* threadGroup;
1938 Object* parent;
1939
1940 threadGroup = objectIdToObject(threadGroupId);
1941 assert(threadGroup != NULL);
1942
1943 parent = dvmGetFieldObject(threadGroup, gDvm.offJavaLangThreadGroup_parent);
1944 return objectToObjectId(parent);
1945 }
1946
1947 /*
1948 * Get the list of threads in the thread group.
1949 *
1950 * We do this by running through the full list of threads and returning
1951 * the ones that have the ThreadGroup object as their owner.
1952 *
1953 * If threadGroupId is set to "kAllThreads", we ignore the group field and
1954 * return all threads.
1955 *
1956 * The caller must free "*ppThreadIds".
1957 */
dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,ObjectId ** ppThreadIds,u4 * pThreadCount)1958 void dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,
1959 ObjectId** ppThreadIds, u4* pThreadCount)
1960 {
1961 Object* targetThreadGroup = NULL;
1962 Thread* thread;
1963 int count;
1964
1965 if (threadGroupId != THREAD_GROUP_ALL) {
1966 targetThreadGroup = objectIdToObject(threadGroupId);
1967 assert(targetThreadGroup != NULL);
1968 }
1969
1970 dvmLockThreadList(NULL);
1971
1972 thread = gDvm.threadList;
1973 count = 0;
1974 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
1975 Object* group;
1976
1977 /* Skip over the JDWP support thread. Some debuggers
1978 * get bent out of shape when they can't suspend and
1979 * query all threads, so it's easier if we just don't
1980 * tell them about us.
1981 */
1982 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
1983 continue;
1984
1985 /* This thread is currently being created, and isn't ready
1986 * to be seen by the debugger yet.
1987 */
1988 if (thread->threadObj == NULL)
1989 continue;
1990
1991 group = dvmGetFieldObject(thread->threadObj,
1992 gDvm.offJavaLangThread_group);
1993 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
1994 count++;
1995 }
1996
1997 *pThreadCount = count;
1998
1999 if (count == 0) {
2000 *ppThreadIds = NULL;
2001 } else {
2002 ObjectId* ptr;
2003 ptr = *ppThreadIds = (ObjectId*) malloc(sizeof(ObjectId) * count);
2004
2005 for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
2006 Object* group;
2007
2008 /* Skip over the JDWP support thread. Some debuggers
2009 * get bent out of shape when they can't suspend and
2010 * query all threads, so it's easier if we just don't
2011 * tell them about us.
2012 */
2013 if (thread->handle == dvmJdwpGetDebugThread(gDvm.jdwpState))
2014 continue;
2015
2016 /* This thread is currently being created, and isn't ready
2017 * to be seen by the debugger yet.
2018 */
2019 if (thread->threadObj == NULL)
2020 continue;
2021
2022 group = dvmGetFieldObject(thread->threadObj,
2023 gDvm.offJavaLangThread_group);
2024 if (threadGroupId == THREAD_GROUP_ALL || group == targetThreadGroup)
2025 {
2026 *ptr++ = objectToObjectId(thread->threadObj);
2027 count--;
2028 }
2029 }
2030
2031 assert(count == 0);
2032 }
2033
2034 dvmUnlockThreadList();
2035 }
2036
2037 /*
2038 * Get all threads.
2039 *
2040 * The caller must free "*ppThreadIds".
2041 */
dvmDbgGetAllThreads(ObjectId ** ppThreadIds,u4 * pThreadCount)2042 void dvmDbgGetAllThreads(ObjectId** ppThreadIds, u4* pThreadCount)
2043 {
2044 dvmDbgGetThreadGroupThreads(THREAD_GROUP_ALL, ppThreadIds, pThreadCount);
2045 }
2046
2047
2048 /*
2049 * Count up the #of frames on the thread's stack.
2050 *
2051 * Returns -1 on failure.
2052 */
dvmDbgGetThreadFrameCount(ObjectId threadId)2053 int dvmDbgGetThreadFrameCount(ObjectId threadId)
2054 {
2055 Object* threadObj;
2056 Thread* thread;
2057 int count = -1;
2058
2059 threadObj = objectIdToObject(threadId);
2060
2061 dvmLockThreadList(NULL);
2062 thread = threadObjToThread(threadObj);
2063 if (thread != NULL) {
2064 count = dvmComputeExactFrameDepth(thread->interpSave.curFrame);
2065 }
2066 dvmUnlockThreadList();
2067
2068 return count;
2069 }
2070
2071 /*
2072 * Get info for frame N from the specified thread's stack.
2073 */
dvmDbgGetThreadFrame(ObjectId threadId,int num,FrameId * pFrameId,JdwpLocation * pLoc)2074 bool dvmDbgGetThreadFrame(ObjectId threadId, int num, FrameId* pFrameId,
2075 JdwpLocation* pLoc)
2076 {
2077 Object* threadObj;
2078 Thread* thread;
2079 void* framePtr;
2080 int count;
2081
2082 threadObj = objectIdToObject(threadId);
2083
2084 dvmLockThreadList(NULL);
2085
2086 thread = threadObjToThread(threadObj);
2087 if (thread == NULL)
2088 goto bail;
2089
2090 framePtr = thread->interpSave.curFrame;
2091 count = 0;
2092 while (framePtr != NULL) {
2093 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2094 const Method* method = saveArea->method;
2095
2096 if (!dvmIsBreakFrame((u4*)framePtr)) {
2097 if (count == num) {
2098 *pFrameId = frameToFrameId(framePtr);
2099 if (dvmIsInterfaceClass(method->clazz))
2100 pLoc->typeTag = TT_INTERFACE;
2101 else
2102 pLoc->typeTag = TT_CLASS;
2103 pLoc->classId = classObjectToRefTypeId(method->clazz);
2104 pLoc->methodId = methodToMethodId(method);
2105 if (dvmIsNativeMethod(method))
2106 pLoc->idx = (u8)-1;
2107 else
2108 pLoc->idx = saveArea->xtra.currentPc - method->insns;
2109 dvmUnlockThreadList();
2110 return true;
2111 }
2112
2113 count++;
2114 }
2115
2116 framePtr = saveArea->prevFrame;
2117 }
2118
2119 bail:
2120 dvmUnlockThreadList();
2121 return false;
2122 }
2123
2124 /*
2125 * Get the ThreadId for the current thread.
2126 */
dvmDbgGetThreadSelfId()2127 ObjectId dvmDbgGetThreadSelfId()
2128 {
2129 Thread* self = dvmThreadSelf();
2130 return objectToObjectId(self->threadObj);
2131 }
2132
2133 /*
2134 * Suspend the VM.
2135 */
dvmDbgSuspendVM(bool isEvent)2136 void dvmDbgSuspendVM(bool isEvent)
2137 {
2138 dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
2139 }
2140
2141 /*
2142 * Resume the VM.
2143 */
dvmDbgResumeVM()2144 void dvmDbgResumeVM()
2145 {
2146 dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
2147 }
2148
2149 /*
2150 * Suspend one thread (not ourselves).
2151 */
dvmDbgSuspendThread(ObjectId threadId)2152 void dvmDbgSuspendThread(ObjectId threadId)
2153 {
2154 Object* threadObj = objectIdToObject(threadId);
2155 Thread* thread;
2156
2157 dvmLockThreadList(NULL);
2158
2159 thread = threadObjToThread(threadObj);
2160 if (thread == NULL) {
2161 /* can happen if our ThreadDeath notify crosses in the mail */
2162 LOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
2163 } else {
2164 dvmSuspendThread(thread);
2165 }
2166
2167 dvmUnlockThreadList();
2168 }
2169
2170 /*
2171 * Resume one thread (not ourselves).
2172 */
dvmDbgResumeThread(ObjectId threadId)2173 void dvmDbgResumeThread(ObjectId threadId)
2174 {
2175 Object* threadObj = objectIdToObject(threadId);
2176 Thread* thread;
2177
2178 dvmLockThreadList(NULL);
2179
2180 thread = threadObjToThread(threadObj);
2181 if (thread == NULL) {
2182 LOGW("WARNING: threadid=%llx obj=%p no match", threadId, threadObj);
2183 } else {
2184 dvmResumeThread(thread);
2185 }
2186
2187 dvmUnlockThreadList();
2188 }
2189
2190 /*
2191 * Suspend ourselves after sending an event to the debugger.
2192 */
dvmDbgSuspendSelf()2193 void dvmDbgSuspendSelf()
2194 {
2195 dvmSuspendSelf(true);
2196 }
2197
2198 /*
2199 * Get the "this" object for the specified frame.
2200 */
getThisObject(const u4 * framePtr)2201 static Object* getThisObject(const u4* framePtr)
2202 {
2203 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr);
2204 const Method* method = saveArea->method;
2205 int argOffset = method->registersSize - method->insSize;
2206 Object* thisObj;
2207
2208 if (method == NULL) {
2209 /* this is a "break" frame? */
2210 assert(false);
2211 return NULL;
2212 }
2213
2214 LOGVV(" Pulling this object for frame at %p", framePtr);
2215 LOGVV(" Method='%s' native=%d static=%d this=%p",
2216 method->name, dvmIsNativeMethod(method),
2217 dvmIsStaticMethod(method), (Object*) framePtr[argOffset]);
2218
2219 /*
2220 * No "this" pointer for statics. No args on the interp stack for
2221 * native methods invoked directly from the VM.
2222 */
2223 if (dvmIsNativeMethod(method) || dvmIsStaticMethod(method))
2224 thisObj = NULL;
2225 else
2226 thisObj = (Object*) framePtr[argOffset];
2227
2228 if (thisObj != NULL && !dvmIsHeapAddress(thisObj)) {
2229 LOGW("Debugger: invalid 'this' pointer %p in %s.%s; returning NULL",
2230 framePtr, method->clazz->descriptor, method->name);
2231 thisObj = NULL;
2232 }
2233
2234 return thisObj;
2235 }
2236
2237 /*
2238 * Return the "this" object for the specified frame. The thread must be
2239 * suspended.
2240 */
dvmDbgGetThisObject(ObjectId threadId,FrameId frameId,ObjectId * pThisId)2241 bool dvmDbgGetThisObject(ObjectId threadId, FrameId frameId, ObjectId* pThisId)
2242 {
2243 const u4* framePtr = frameIdToFrame(frameId);
2244 Object* thisObj;
2245
2246 UNUSED_PARAMETER(threadId);
2247
2248 thisObj = getThisObject(framePtr);
2249
2250 *pThisId = objectToObjectId(thisObj);
2251 return true;
2252 }
2253
2254 /*
2255 * Copy the value of a method argument or local variable into the
2256 * specified buffer. The value will be preceeded with the tag.
2257 *
2258 * The debugger includes the tags in the request. Object tags may
2259 * be updated with a more refined type.
2260 */
dvmDbgGetLocalValue(ObjectId threadId,FrameId frameId,int slot,u1 tag,u1 * buf,int expectedLen)2261 void dvmDbgGetLocalValue(ObjectId threadId, FrameId frameId, int slot,
2262 u1 tag, u1* buf, int expectedLen)
2263 {
2264 const u4* framePtr = frameIdToFrame(frameId);
2265 Object* objVal;
2266 u4 intVal;
2267 u8 longVal;
2268
2269 UNUSED_PARAMETER(threadId);
2270
2271 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2272
2273 switch (tag) {
2274 case JT_BOOLEAN:
2275 assert(expectedLen == 1);
2276 intVal = framePtr[slot];
2277 set1(buf+1, intVal != 0);
2278 break;
2279 case JT_BYTE:
2280 assert(expectedLen == 1);
2281 intVal = framePtr[slot];
2282 set1(buf+1, intVal);
2283 break;
2284 case JT_SHORT:
2285 case JT_CHAR:
2286 assert(expectedLen == 2);
2287 intVal = framePtr[slot];
2288 set2BE(buf+1, intVal);
2289 break;
2290 case JT_INT:
2291 case JT_FLOAT:
2292 assert(expectedLen == 4);
2293 intVal = framePtr[slot];
2294 set4BE(buf+1, intVal);
2295 break;
2296 case JT_ARRAY:
2297 assert(expectedLen == sizeof(ObjectId));
2298 {
2299 /* convert to "ObjectId" */
2300 objVal = (Object*)framePtr[slot];
2301 if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
2302 LOGW("JDWP: slot %d expected to hold array, %p invalid",
2303 slot, objVal);
2304 dvmAbort(); // DEBUG: make it obvious
2305 objVal = NULL;
2306 tag = JT_OBJECT; // JT_ARRAY not expected for NULL ref
2307 }
2308 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2309 }
2310 break;
2311 case JT_OBJECT:
2312 assert(expectedLen == sizeof(ObjectId));
2313 {
2314 /* convert to "ObjectId" */
2315 objVal = (Object*)framePtr[slot];
2316
2317 if (objVal != NULL && !dvmIsHeapAddress(objVal)) {
2318 LOGW("JDWP: slot %d expected to hold object, %p invalid",
2319 slot, objVal);
2320 dvmAbort(); // DEBUG: make it obvious
2321 objVal = NULL;
2322 }
2323 tag = tagFromObject(objVal);
2324 dvmSetObjectId(buf+1, objectToObjectId(objVal));
2325 }
2326 break;
2327 case JT_DOUBLE:
2328 case JT_LONG:
2329 assert(expectedLen == 8);
2330 memcpy(&longVal, &framePtr[slot], 8);
2331 set8BE(buf+1, longVal);
2332 break;
2333 default:
2334 LOGE("ERROR: unhandled tag '%c'", tag);
2335 assert(false);
2336 break;
2337 }
2338
2339 /* prepend tag, which may have been updated */
2340 set1(buf, tag);
2341 }
2342
2343 /*
2344 * Copy a new value into an argument or local variable.
2345 */
dvmDbgSetLocalValue(ObjectId threadId,FrameId frameId,int slot,u1 tag,u8 value,int width)2346 void dvmDbgSetLocalValue(ObjectId threadId, FrameId frameId, int slot, u1 tag,
2347 u8 value, int width)
2348 {
2349 u4* framePtr = frameIdToFrame(frameId);
2350
2351 UNUSED_PARAMETER(threadId);
2352
2353 slot = untweakSlot(slot, framePtr); // Eclipse workaround
2354
2355 switch (tag) {
2356 case JT_BOOLEAN:
2357 assert(width == 1);
2358 framePtr[slot] = (u4)value;
2359 break;
2360 case JT_BYTE:
2361 assert(width == 1);
2362 framePtr[slot] = (u4)value;
2363 break;
2364 case JT_SHORT:
2365 case JT_CHAR:
2366 assert(width == 2);
2367 framePtr[slot] = (u4)value;
2368 break;
2369 case JT_INT:
2370 case JT_FLOAT:
2371 assert(width == 4);
2372 framePtr[slot] = (u4)value;
2373 break;
2374 case JT_STRING:
2375 /* The debugger calls VirtualMachine.CreateString to create a new
2376 * string, then uses this to set the object reference, when you
2377 * edit a String object */
2378 case JT_ARRAY:
2379 case JT_OBJECT:
2380 assert(width == sizeof(ObjectId));
2381 framePtr[slot] = (u4) objectIdToObject(value);
2382 break;
2383 case JT_DOUBLE:
2384 case JT_LONG:
2385 assert(width == 8);
2386 memcpy(&framePtr[slot], &value, 8);
2387 break;
2388 case JT_VOID:
2389 case JT_CLASS_OBJECT:
2390 case JT_THREAD:
2391 case JT_THREAD_GROUP:
2392 case JT_CLASS_LOADER:
2393 /* not expecting these from debugger; fall through to failure */
2394 default:
2395 LOGE("ERROR: unhandled tag '%c'", tag);
2396 assert(false);
2397 break;
2398 }
2399 }
2400
2401
2402 /*
2403 * ===========================================================================
2404 * Debugger notification
2405 * ===========================================================================
2406 */
2407
2408 /*
2409 * Tell JDWP that a breakpoint address has been reached.
2410 *
2411 * "pcOffset" will be -1 for native methods.
2412 * "thisPtr" will be NULL for static methods.
2413 */
dvmDbgPostLocationEvent(const Method * method,int pcOffset,Object * thisPtr,int eventFlags)2414 void dvmDbgPostLocationEvent(const Method* method, int pcOffset,
2415 Object* thisPtr, int eventFlags)
2416 {
2417 JdwpLocation loc;
2418
2419 if (dvmIsInterfaceClass(method->clazz))
2420 loc.typeTag = TT_INTERFACE;
2421 else
2422 loc.typeTag = TT_CLASS;
2423 loc.classId = classObjectToRefTypeId(method->clazz);
2424 loc.methodId = methodToMethodId(method);
2425 loc.idx = pcOffset;
2426
2427 /*
2428 * Note we use "NoReg" so we don't keep track of references that are
2429 * never actually sent to the debugger. The "thisPtr" is only used to
2430 * compare against registered events.
2431 */
2432
2433 if (dvmJdwpPostLocationEvent(gDvm.jdwpState, &loc,
2434 objectToObjectIdNoReg(thisPtr), eventFlags))
2435 {
2436 classObjectToRefTypeId(method->clazz);
2437 objectToObjectId(thisPtr);
2438 }
2439 }
2440
2441 /*
2442 * Tell JDWP that an exception has occurred.
2443 */
dvmDbgPostException(void * throwFp,int throwRelPc,void * catchFp,int catchRelPc,Object * exception)2444 void dvmDbgPostException(void* throwFp, int throwRelPc, void* catchFp,
2445 int catchRelPc, Object* exception)
2446 {
2447 JdwpLocation throwLoc, catchLoc;
2448 const Method* throwMeth;
2449 const Method* catchMeth;
2450
2451 throwMeth = SAVEAREA_FROM_FP(throwFp)->method;
2452 if (dvmIsInterfaceClass(throwMeth->clazz))
2453 throwLoc.typeTag = TT_INTERFACE;
2454 else
2455 throwLoc.typeTag = TT_CLASS;
2456 throwLoc.classId = classObjectToRefTypeId(throwMeth->clazz);
2457 throwLoc.methodId = methodToMethodId(throwMeth);
2458 throwLoc.idx = throwRelPc;
2459
2460 if (catchRelPc < 0) {
2461 memset(&catchLoc, 0, sizeof(catchLoc));
2462 } else {
2463 catchMeth = SAVEAREA_FROM_FP(catchFp)->method;
2464 if (dvmIsInterfaceClass(catchMeth->clazz))
2465 catchLoc.typeTag = TT_INTERFACE;
2466 else
2467 catchLoc.typeTag = TT_CLASS;
2468 catchLoc.classId = classObjectToRefTypeId(catchMeth->clazz);
2469 catchLoc.methodId = methodToMethodId(catchMeth);
2470 catchLoc.idx = catchRelPc;
2471 }
2472
2473 /* need this for InstanceOnly filters */
2474 Object* thisObj = getThisObject((u4*)throwFp);
2475
2476 /*
2477 * Hand the event to the JDWP exception handler. Note we're using the
2478 * "NoReg" objectID on the exception, which is not strictly correct --
2479 * the exception object WILL be passed up to the debugger if the
2480 * debugger is interested in the event. We do this because the current
2481 * implementation of the debugger object registry never throws anything
2482 * away, and some people were experiencing a fatal build up of exception
2483 * objects when dealing with certain libraries.
2484 */
2485 dvmJdwpPostException(gDvm.jdwpState, &throwLoc,
2486 objectToObjectIdNoReg(exception),
2487 classObjectToRefTypeId(exception->clazz), &catchLoc,
2488 objectToObjectId(thisObj));
2489 }
2490
2491 /*
2492 * Tell JDWP and/or DDMS that a thread has started.
2493 */
dvmDbgPostThreadStart(Thread * thread)2494 void dvmDbgPostThreadStart(Thread* thread)
2495 {
2496 if (gDvm.debuggerActive) {
2497 dvmJdwpPostThreadChange(gDvm.jdwpState,
2498 objectToObjectId(thread->threadObj), true);
2499 }
2500 if (gDvm.ddmThreadNotification)
2501 dvmDdmSendThreadNotification(thread, true);
2502 }
2503
2504 /*
2505 * Tell JDWP and/or DDMS that a thread has gone away.
2506 */
dvmDbgPostThreadDeath(Thread * thread)2507 void dvmDbgPostThreadDeath(Thread* thread)
2508 {
2509 if (gDvm.debuggerActive) {
2510 dvmJdwpPostThreadChange(gDvm.jdwpState,
2511 objectToObjectId(thread->threadObj), false);
2512 }
2513 if (gDvm.ddmThreadNotification)
2514 dvmDdmSendThreadNotification(thread, false);
2515 }
2516
2517 /*
2518 * Tell JDWP that a new class has been prepared.
2519 */
dvmDbgPostClassPrepare(ClassObject * clazz)2520 void dvmDbgPostClassPrepare(ClassObject* clazz)
2521 {
2522 const char* signature;
2523 int tag;
2524
2525 if (dvmIsInterfaceClass(clazz))
2526 tag = TT_INTERFACE;
2527 else
2528 tag = TT_CLASS;
2529
2530 // TODO - we currently always send both "verified" and "prepared" since
2531 // debuggers seem to like that. There might be some advantage to honesty,
2532 // since the class may not yet be verified.
2533 signature = jniSignature(clazz);
2534 dvmJdwpPostClassPrepare(gDvm.jdwpState, tag, classObjectToRefTypeId(clazz),
2535 signature, CS_VERIFIED | CS_PREPARED);
2536 }
2537
2538 /*
2539 * The JDWP event mechanism has registered an event with a LocationOnly
2540 * mod. Tell the interpreter to call us if we hit the specified
2541 * address.
2542 */
dvmDbgWatchLocation(const JdwpLocation * pLoc)2543 bool dvmDbgWatchLocation(const JdwpLocation* pLoc)
2544 {
2545 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2546 assert(!dvmIsNativeMethod(method));
2547 dvmAddBreakAddr(method, pLoc->idx);
2548 return true; /* assume success */
2549 }
2550
2551 /*
2552 * An event with a LocationOnly mod has been removed.
2553 */
dvmDbgUnwatchLocation(const JdwpLocation * pLoc)2554 void dvmDbgUnwatchLocation(const JdwpLocation* pLoc)
2555 {
2556 Method* method = methodIdToMethod(pLoc->classId, pLoc->methodId);
2557 assert(!dvmIsNativeMethod(method));
2558 dvmClearBreakAddr(method, pLoc->idx);
2559 }
2560
2561 /*
2562 * The JDWP event mechanism has registered a single-step event. Tell
2563 * the interpreter about it.
2564 */
dvmDbgConfigureStep(ObjectId threadId,JdwpStepSize size,JdwpStepDepth depth)2565 bool dvmDbgConfigureStep(ObjectId threadId, JdwpStepSize size,
2566 JdwpStepDepth depth)
2567 {
2568 Object* threadObj;
2569 Thread* thread;
2570 bool result = false;
2571
2572 threadObj = objectIdToObject(threadId);
2573 assert(threadObj != NULL);
2574
2575 /*
2576 * Get a pointer to the Thread struct for this ID. The pointer will
2577 * be used strictly for comparisons against the current thread pointer
2578 * after the setup is complete, so we can safely release the lock.
2579 */
2580 dvmLockThreadList(NULL);
2581 thread = threadObjToThread(threadObj);
2582
2583 if (thread == NULL) {
2584 LOGE("Thread for single-step not found");
2585 goto bail;
2586 }
2587 if (!dvmIsSuspended(thread)) {
2588 LOGE("Thread for single-step not suspended");
2589 assert(!"non-susp step"); // I want to know if this can happen
2590 goto bail;
2591 }
2592
2593 assert(dvmIsSuspended(thread));
2594 if (!dvmAddSingleStep(thread, size, depth))
2595 goto bail;
2596
2597 result = true;
2598
2599 bail:
2600 dvmUnlockThreadList();
2601 return result;
2602 }
2603
2604 /*
2605 * A single-step event has been removed.
2606 */
dvmDbgUnconfigureStep(ObjectId threadId)2607 void dvmDbgUnconfigureStep(ObjectId threadId)
2608 {
2609 UNUSED_PARAMETER(threadId);
2610
2611 /* right now it's global, so don't need to find Thread */
2612 dvmClearSingleStep(NULL);
2613 }
2614
2615 /*
2616 * Invoke a method in a thread that has been stopped on a breakpoint or
2617 * other debugger event. (This function is called from the JDWP thread.)
2618 *
2619 * Note that access control is not enforced, per spec.
2620 */
dvmDbgInvokeMethod(ObjectId threadId,ObjectId objectId,RefTypeId classId,MethodId methodId,u4 numArgs,ObjectId * argArray,u4 options,u1 * pResultTag,u8 * pResultValue,ObjectId * pExceptObj)2621 JdwpError dvmDbgInvokeMethod(ObjectId threadId, ObjectId objectId,
2622 RefTypeId classId, MethodId methodId, u4 numArgs, ObjectId* argArray,
2623 u4 options, u1* pResultTag, u8* pResultValue, ObjectId* pExceptObj)
2624 {
2625 Object* threadObj = objectIdToObject(threadId);
2626
2627 dvmLockThreadList(NULL);
2628
2629 Thread* targetThread = threadObjToThread(threadObj);
2630 if (targetThread == NULL) {
2631 dvmUnlockThreadList();
2632 return ERR_INVALID_THREAD; /* thread does not exist */
2633 }
2634 if (!targetThread->invokeReq.ready) {
2635 dvmUnlockThreadList();
2636 return ERR_INVALID_THREAD; /* thread not stopped by event */
2637 }
2638
2639 /*
2640 * We currently have a bug where we don't successfully resume the
2641 * target thread if the suspend count is too deep. We're expected to
2642 * require one "resume" for each "suspend", but when asked to execute
2643 * a method we have to resume fully and then re-suspend it back to the
2644 * same level. (The easiest way to cause this is to type "suspend"
2645 * multiple times in jdb.)
2646 *
2647 * It's unclear what this means when the event specifies "resume all"
2648 * and some threads are suspended more deeply than others. This is
2649 * a rare problem, so for now we just prevent it from hanging forever
2650 * by rejecting the method invocation request. Without this, we will
2651 * be stuck waiting on a suspended thread.
2652 */
2653 if (targetThread->suspendCount > 1) {
2654 LOGW("threadid=%d: suspend count on threadid=%d is %d, too deep "
2655 "for method exec",
2656 dvmThreadSelf()->threadId, targetThread->threadId,
2657 targetThread->suspendCount);
2658 dvmUnlockThreadList();
2659 return ERR_THREAD_SUSPENDED; /* probably not expected here */
2660 }
2661
2662 /*
2663 * TODO: ought to screen the various IDs, and verify that the argument
2664 * list is valid.
2665 */
2666
2667 targetThread->invokeReq.obj = objectIdToObject(objectId);
2668 targetThread->invokeReq.thread = threadObj;
2669 targetThread->invokeReq.clazz = refTypeIdToClassObject(classId);
2670 targetThread->invokeReq.method = methodIdToMethod(classId, methodId);
2671 targetThread->invokeReq.numArgs = numArgs;
2672 targetThread->invokeReq.argArray = argArray;
2673 targetThread->invokeReq.options = options;
2674 targetThread->invokeReq.invokeNeeded = true;
2675
2676 /*
2677 * This is a bit risky -- if the thread goes away we're sitting high
2678 * and dry -- but we must release this before the dvmResumeAllThreads
2679 * call, and it's unwise to hold it during dvmWaitForSuspend.
2680 */
2681 dvmUnlockThreadList();
2682
2683 /*
2684 * We change our (JDWP thread) status, which should be THREAD_RUNNING,
2685 * so the VM can suspend for a GC if the invoke request causes us to
2686 * run out of memory. It's also a good idea to change it before locking
2687 * the invokeReq mutex, although that should never be held for long.
2688 */
2689 Thread* self = dvmThreadSelf();
2690 ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
2691
2692 LOGV(" Transferring control to event thread");
2693 dvmLockMutex(&targetThread->invokeReq.lock);
2694
2695 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2696 LOGV(" Resuming all threads");
2697 dvmResumeAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2698 } else {
2699 LOGV(" Resuming event thread only");
2700 dvmResumeThread(targetThread);
2701 }
2702
2703 /*
2704 * Wait for the request to finish executing.
2705 */
2706 while (targetThread->invokeReq.invokeNeeded) {
2707 pthread_cond_wait(&targetThread->invokeReq.cv,
2708 &targetThread->invokeReq.lock);
2709 }
2710 dvmUnlockMutex(&targetThread->invokeReq.lock);
2711 LOGV(" Control has returned from event thread");
2712
2713 /* wait for thread to re-suspend itself */
2714 dvmWaitForSuspend(targetThread);
2715
2716 /*
2717 * Done waiting, switch back to RUNNING.
2718 */
2719 dvmChangeStatus(self, oldStatus);
2720
2721 /*
2722 * Suspend the threads. We waited for the target thread to suspend
2723 * itself, so all we need to do is suspend the others.
2724 *
2725 * The suspendAllThreads() call will double-suspend the event thread,
2726 * so we want to resume the target thread once to keep the books straight.
2727 */
2728 if ((options & INVOKE_SINGLE_THREADED) == 0) {
2729 LOGV(" Suspending all threads");
2730 dvmSuspendAllThreads(SUSPEND_FOR_DEBUG_EVENT);
2731 LOGV(" Resuming event thread to balance the count");
2732 dvmResumeThread(targetThread);
2733 }
2734
2735 /*
2736 * Set up the result.
2737 */
2738 *pResultTag = targetThread->invokeReq.resultTag;
2739 if (isTagPrimitive(targetThread->invokeReq.resultTag))
2740 *pResultValue = targetThread->invokeReq.resultValue.j;
2741 else {
2742 Object* tmpObj = (Object*)targetThread->invokeReq.resultValue.l;
2743 *pResultValue = objectToObjectId(tmpObj);
2744 }
2745 *pExceptObj = targetThread->invokeReq.exceptObj;
2746 return targetThread->invokeReq.err;
2747 }
2748
2749 /*
2750 * Return a basic tag value for the return type.
2751 */
getReturnTypeBasicTag(const Method * method)2752 static u1 getReturnTypeBasicTag(const Method* method)
2753 {
2754 const char* descriptor = dexProtoGetReturnType(&method->prototype);
2755 return basicTagFromDescriptor(descriptor);
2756 }
2757
2758 /*
2759 * Execute the method described by "*pReq".
2760 *
2761 * We're currently in VMWAIT, because we're stopped on a breakpoint. We
2762 * want to switch to RUNNING while we execute.
2763 */
dvmDbgExecuteMethod(DebugInvokeReq * pReq)2764 void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
2765 {
2766 Thread* self = dvmThreadSelf();
2767 const Method* meth;
2768 Object* oldExcept;
2769 ThreadStatus oldStatus;
2770
2771 /*
2772 * We can be called while an exception is pending in the VM. We need
2773 * to preserve that across the method invocation.
2774 */
2775 oldExcept = dvmGetException(self);
2776 if (oldExcept != NULL) {
2777 dvmAddTrackedAlloc(oldExcept, self);
2778 dvmClearException(self);
2779 }
2780
2781 oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
2782
2783 /*
2784 * Translate the method through the vtable, unless we're calling a
2785 * direct method or the debugger wants to suppress it.
2786 */
2787 if ((pReq->options & INVOKE_NONVIRTUAL) != 0 || pReq->obj == NULL ||
2788 dvmIsDirectMethod(pReq->method))
2789 {
2790 meth = pReq->method;
2791 } else {
2792 meth = dvmGetVirtualizedMethod(pReq->clazz, pReq->method);
2793 }
2794 assert(meth != NULL);
2795
2796 assert(sizeof(jvalue) == sizeof(u8));
2797
2798 IF_LOGV() {
2799 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2800 LOGV("JDWP invoking method %p/%p %s.%s:%s",
2801 pReq->method, meth, meth->clazz->descriptor, meth->name, desc);
2802 free(desc);
2803 }
2804
2805 dvmCallMethodA(self, meth, pReq->obj, false, &pReq->resultValue,
2806 (jvalue*)pReq->argArray);
2807 pReq->exceptObj = objectToObjectId(dvmGetException(self));
2808 pReq->resultTag = getReturnTypeBasicTag(meth);
2809 if (pReq->exceptObj != 0) {
2810 Object* exc = dvmGetException(self);
2811 LOGD(" JDWP invocation returning with exceptObj=%p (%s)",
2812 exc, exc->clazz->descriptor);
2813 //dvmLogExceptionStackTrace();
2814 dvmClearException(self);
2815 /*
2816 * Nothing should try to use this, but it looks like something is.
2817 * Make it null to be safe.
2818 */
2819 pReq->resultValue.j = 0; /*0xadadadad;*/
2820 } else if (pReq->resultTag == JT_OBJECT) {
2821 /* if no exception thrown, examine object result more closely */
2822 u1 newTag = tagFromObject((Object*)pReq->resultValue.l);
2823 if (newTag != pReq->resultTag) {
2824 LOGVV(" JDWP promoted result from %d to %d",
2825 pReq->resultTag, newTag);
2826 pReq->resultTag = newTag;
2827 }
2828
2829 /*
2830 * Register the object. We don't actually need an ObjectId yet,
2831 * but we do need to be sure that the GC won't move or discard the
2832 * object when we switch out of RUNNING. The ObjectId conversion
2833 * will add the object to the "do not touch" list.
2834 *
2835 * We can't use the "tracked allocation" mechanism here because
2836 * the object is going to be handed off to a different thread.
2837 */
2838 objectToObjectId((Object*)pReq->resultValue.l);
2839 }
2840
2841 if (oldExcept != NULL) {
2842 dvmSetException(self, oldExcept);
2843 dvmReleaseTrackedAlloc(oldExcept, self);
2844 }
2845 dvmChangeStatus(self, oldStatus);
2846 }
2847
2848 // for dvmAddressSetForLine
2849 struct AddressSetContext {
2850 bool lastAddressValid;
2851 u4 lastAddress;
2852 u4 lineNum;
2853 AddressSet *pSet;
2854 };
2855
2856 // for dvmAddressSetForLine
addressSetCb(void * cnxt,u4 address,u4 lineNum)2857 static int addressSetCb (void *cnxt, u4 address, u4 lineNum)
2858 {
2859 AddressSetContext *pContext = (AddressSetContext *)cnxt;
2860
2861 if (lineNum == pContext->lineNum) {
2862 if (!pContext->lastAddressValid) {
2863 // Everything from this address until the next line change is ours
2864 pContext->lastAddress = address;
2865 pContext->lastAddressValid = true;
2866 }
2867 // else, If we're already in a valid range for this lineNum,
2868 // just keep going (shouldn't really happen)
2869 } else if (pContext->lastAddressValid) { // and the line number is new
2870 u4 i;
2871 // Add everything from the last entry up until here to the set
2872 for (i = pContext->lastAddress; i < address; i++) {
2873 dvmAddressSetSet(pContext->pSet, i);
2874 }
2875
2876 pContext->lastAddressValid = false;
2877 }
2878
2879 // there may be multiple entries for a line
2880 return 0;
2881 }
2882 /*
2883 * Build up a set of bytecode addresses associated with a line number
2884 */
dvmAddressSetForLine(const Method * method,int line)2885 const AddressSet *dvmAddressSetForLine(const Method* method, int line)
2886 {
2887 AddressSet *result;
2888 const DexFile *pDexFile = method->clazz->pDvmDex->pDexFile;
2889 u4 insnsSize = dvmGetMethodInsnsSize(method);
2890 AddressSetContext context;
2891
2892 result = (AddressSet*)calloc(1, sizeof(AddressSet) + (insnsSize/8) + 1);
2893 result->setSize = insnsSize;
2894
2895 memset(&context, 0, sizeof(context));
2896 context.pSet = result;
2897 context.lineNum = line;
2898 context.lastAddressValid = false;
2899
2900 dexDecodeDebugInfo(pDexFile, dvmGetMethodCode(method),
2901 method->clazz->descriptor,
2902 method->prototype.protoIdx,
2903 method->accessFlags,
2904 addressSetCb, NULL, &context);
2905
2906 // If the line number was the last in the position table...
2907 if (context.lastAddressValid) {
2908 u4 i;
2909 for (i = context.lastAddress; i < insnsSize; i++) {
2910 dvmAddressSetSet(result, i);
2911 }
2912 }
2913
2914 return result;
2915 }
2916
2917
2918 /*
2919 * ===========================================================================
2920 * Dalvik Debug Monitor support
2921 * ===========================================================================
2922 */
2923
2924 /*
2925 * We have received a DDM packet over JDWP. Hand it off to the VM.
2926 */
dvmDbgDdmHandlePacket(const u1 * buf,int dataLen,u1 ** pReplyBuf,int * pReplyLen)2927 bool dvmDbgDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
2928 int* pReplyLen)
2929 {
2930 return dvmDdmHandlePacket(buf, dataLen, pReplyBuf, pReplyLen);
2931 }
2932
2933 /*
2934 * First DDM packet has arrived over JDWP. Notify the press.
2935 */
dvmDbgDdmConnected()2936 void dvmDbgDdmConnected()
2937 {
2938 dvmDdmConnected();
2939 }
2940
2941 /*
2942 * JDWP connection has dropped.
2943 */
dvmDbgDdmDisconnected()2944 void dvmDbgDdmDisconnected()
2945 {
2946 dvmDdmDisconnected();
2947 }
2948
2949 /*
2950 * Send up a JDWP event packet with a DDM chunk in it.
2951 */
dvmDbgDdmSendChunk(int type,size_t len,const u1 * buf)2952 void dvmDbgDdmSendChunk(int type, size_t len, const u1* buf)
2953 {
2954 assert(buf != NULL);
2955 struct iovec vec[1] = { {(void*)buf, len} };
2956 dvmDbgDdmSendChunkV(type, vec, 1);
2957 }
2958
2959 /*
2960 * Send up a JDWP event packet with a DDM chunk in it. The chunk is
2961 * concatenated from multiple source buffers.
2962 */
dvmDbgDdmSendChunkV(int type,const struct iovec * iov,int iovcnt)2963 void dvmDbgDdmSendChunkV(int type, const struct iovec* iov, int iovcnt)
2964 {
2965 if (gDvm.jdwpState == NULL) {
2966 LOGV("Debugger thread not active, ignoring DDM send (t=0x%08x)",
2967 type);
2968 return;
2969 }
2970
2971 dvmJdwpDdmSendChunkV(gDvm.jdwpState, type, iov, iovcnt);
2972 }
2973