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