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