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