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