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