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