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