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