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