• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
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  /*
28  Notes on garbage collection and object registration
29  
30  JDWP does not allow the debugger to assume that objects passed to it
31  will not be garbage collected.  It specifies explicit commands (e.g.
32  ObjectReference.DisableCollection) to allow the debugger to manage
33  object lifetime.  It does, however, require that the VM not re-use an
34  object ID unless an explicit "dispose" call has been made, and if the
35  VM asks for a now-collected object we must return INVALID_OBJECT.
36  
37  JDWP also requires that, while the VM is suspended, no garbage collection
38  occur.  The JDWP docs suggest that this is obvious, because no threads
39  can be running.  Unfortunately it's not entirely clear how to deal
40  with situations where the debugger itself allocates strings or executes
41  code as part of displaying variables.  The easiest way to enforce this,
42  short of disabling GC whenever the debugger is connected, is to ensure
43  that the debugger thread can't cause a GC: it has to expand the heap or
44  fail to allocate.  (Might want to make that "is debugger thread AND all
45  other threads are suspended" to avoid unnecessary heap expansion by a
46  poorly-timed JDWP request.)
47  
48  We use an "object registry" so that we can separate our internal
49  representation from what we show the debugger.  This allows us to
50  return a registry table index instead of a pointer or handle.
51  
52  There are various approaches we can take to achieve correct behavior:
53  
54  (1) Disable garbage collection entirely while the debugger is attached.
55  This is very easy, but doesn't allow extended debugging sessions on
56  small devices.
57  
58  (2) Keep a list of all object references requested by or sent to the
59  debugger, and include the list in the GC root set.  This ensures that
60  objects the debugger might care about don't go away.  This is straightforward,
61  but it can cause us to hold on to large objects and prevent finalizers from
62  being executed.
63  
64  (3) Keep a list of what amount to weak object references.  This way we
65  don't interfere with the GC, and can support JDWP requests like
66  "ObjectReference.IsCollected".
67  
68  The current implementation is #2.  The set should be reasonably small and
69  performance isn't critical, so a simple expanding array can be used.
70  
71  
72  Notes on threads:
73  
74  The VM has a Thread struct associated with every active thread.  The
75  ThreadId we pass to the debugger is the ObjectId for the java/lang/Thread
76  object, so to retrieve the VM's Thread struct we have to scan through the
77  list looking for a match.
78  
79  When a thread goes away, we lock the list and free the struct.  To
80  avoid having the thread list updated or Thread structs freed out from
81  under us, we want to acquire and hold the thread list lock while we're
82  performing operations on Threads.  Exceptions to this rule are noted in
83  a couple of places.
84  
85  We can speed this up a bit by adding a Thread struct pointer to the
86  java/lang/Thread object, and ensuring that both are discarded at the
87  same 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   */
dvmDebuggerStartup(void)98  bool dvmDebuggerStartup(void)
99  {
100      gDvm.dbgRegistry = dvmHashTableCreate(1000, NULL);
101      return (gDvm.dbgRegistry != NULL);
102  }
103  
104  /*
105   * Free registry storage.
106   */
dvmDebuggerShutdown(void)107  void 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   */
dvmDbgInitMutex(pthread_mutex_t * pMutex)118  void dvmDbgInitMutex(pthread_mutex_t* pMutex)
119  {
120      dvmInitMutex(pMutex);
121  }
dvmDbgLockMutex(pthread_mutex_t * pMutex)122  void dvmDbgLockMutex(pthread_mutex_t* pMutex)
123  {
124      dvmLockMutex(pMutex);
125  }
dvmDbgUnlockMutex(pthread_mutex_t * pMutex)126  void dvmDbgUnlockMutex(pthread_mutex_t* pMutex)
127  {
128      dvmUnlockMutex(pMutex);
129  }
dvmDbgInitCond(pthread_cond_t * pCond)130  void dvmDbgInitCond(pthread_cond_t* pCond)
131  {
132      pthread_cond_init(pCond, NULL);
133  }
dvmDbgCondWait(pthread_cond_t * pCond,pthread_mutex_t * pMutex)134  void dvmDbgCondWait(pthread_cond_t* pCond, pthread_mutex_t* pMutex)
135  {
136      int cc = pthread_cond_wait(pCond, pMutex);
137      assert(cc == 0);
138  }
dvmDbgCondSignal(pthread_cond_t * pCond)139  void dvmDbgCondSignal(pthread_cond_t* pCond)
140  {
141      int cc = pthread_cond_signal(pCond);
142      assert(cc == 0);
143  }
dvmDbgCondBroadcast(pthread_cond_t * pCond)144  void 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 */
152  typedef 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   */
registryHash(u4 val)161  static inline u4 registryHash(u4 val)
162  {
163      return val >> 4;
164  }
165  
166  /*
167   * (This is a dvmHashTableLookup() callback.)
168   */
registryCompare(const void * obj1,const void * obj2)169  static 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   */
lookupId(ObjectId id)182  static 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   */
registerObject(const Object * obj,RegistryType type,bool reg)203  static 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  
229  bail:
230      dvmHashTableUnlock(gDvm.dbgRegistry);
231      return id;
232  }
233  
234  /*
235   * (This is a HashForeachFunc callback.)
236   */
markRef(void * data,void * arg)237  static 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   */
dvmGcMarkDebuggerRefs()249  void 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   */
objectIsRegistered(ObjectId id,RegistryType type)270  static 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   */
classObjectToRefTypeId(ClassObject * clazz)288  static RefTypeId classObjectToRefTypeId(ClassObject* clazz)
289  {
290      return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, true);
291  }
classObjectToRefTypeIdNoReg(ClassObject * clazz)292  static RefTypeId classObjectToRefTypeIdNoReg(ClassObject* clazz)
293  {
294      return (RefTypeId) registerObject((Object*) clazz, kRefTypeId, false);
295  }
refTypeIdToClassObject(RefTypeId id)296  static 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   */
objectToObjectId(const Object * obj)305  static ObjectId objectToObjectId(const Object* obj)
306  {
307      return registerObject(obj, kObjectId, true);
308  }
objectToObjectIdNoReg(const Object * obj)309  static ObjectId objectToObjectIdNoReg(const Object* obj)
310  {
311      return registerObject(obj, kObjectId, false);
312  }
objectIdToObject(ObjectId id)313  static 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   */
methodToMethodId(const Method * meth)325  static MethodId methodToMethodId(const Method* meth)
326  {
327      return (MethodId)(u4) meth;
328  }
methodIdToMethod(RefTypeId refTypeId,MethodId id)329  static 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   */
fieldToFieldId(const Field * field)341  static FieldId fieldToFieldId(const Field* field)
342  {
343      return (FieldId)(u4) field;
344  }
fieldIdToField(RefTypeId refTypeId,FieldId id)345  static 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   */
frameToFrameId(const void * frame)356  static FrameId frameToFrameId(const void* frame)
357  {
358      return (FrameId)(u4) frame;
359  }
frameIdToFrame(FrameId id)360  static void* frameIdToFrame(FrameId id)
361  {
362      return (void*)(u4) id;
363  }
364  
365  
366  /*
367   * Get the invocation request state.
368   */
dvmDbgGetInvokeReq(void)369  DebugInvokeReq* 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   */
dvmDbgConnected(void)379  void 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   */
dvmDbgActive(void)395  void 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   */
dvmDbgDisconnected(void)413  void 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   */
dvmDbgIsDebuggerConnected(void)437  bool 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   */
dvmDbgLastDebuggerActivity(void)446  s8 dvmDbgLastDebuggerActivity(void)
447  {
448      return dvmJdwpLastDebuggerActivity(gDvm.jdwpState);
449  }
450  
451  /*
452   * JDWP thread is running, don't allow GC.
453   */
dvmDbgThreadRunning(void)454  int dvmDbgThreadRunning(void)
455  {
456      return dvmChangeStatus(NULL, THREAD_RUNNING);
457  }
458  
459  /*
460   * JDWP thread is idle, allow GC.
461   */
dvmDbgThreadWaiting(void)462  int dvmDbgThreadWaiting(void)
463  {
464      return dvmChangeStatus(NULL, THREAD_VMWAIT);
465  }
466  
467  /*
468   * Restore state returned by Running/Waiting calls.
469   */
dvmDbgThreadContinuing(int status)470  int dvmDbgThreadContinuing(int status)
471  {
472      return dvmChangeStatus(NULL, status);
473  }
474  
475  /*
476   * The debugger wants us to exit.
477   */
dvmDbgExit(int status)478  void 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   */
dvmDbgGetClassDescriptor(RefTypeId id)506  const 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   */
dvmDbgGetSuperclass(RefTypeId id)517  RefTypeId 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   */
dvmDbgGetClassLoader(RefTypeId id)526  RefTypeId 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   */
dvmDbgGetAccessFlags(RefTypeId id)535  u4 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   */
dvmDbgIsInterface(RefTypeId id)544  bool dvmDbgIsInterface(RefTypeId id)
545  {
546      ClassObject* clazz = refTypeIdToClassObject(id);
547      return dvmIsInterfaceClass(clazz);
548  }
549  
550  /*
551   * dvmHashForeach callback
552   */
copyRefType(void * vclazz,void * varg)553  static 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   */
dvmDbgGetClassList(u4 * pNumClasses,RefTypeId ** pClassRefBuf)567  void 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   */
dvmDbgGetVisibleClassList(ObjectId classLoaderId,u4 * pNumClasses,RefTypeId ** pClassRefBuf)592  void 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   */
generateJNISignature(ClassObject * clazz)639  static 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   */
dvmDbgGetClassInfo(RefTypeId classId,u1 * pTypeTag,u4 * pStatus,char ** pSignature)650  void 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   */
dvmDbgFindLoadedClassBySignature(const char * classDescriptor,RefTypeId * pRefTypeId)676  bool 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   */
dvmDbgGetObjectType(ObjectId objectId,u1 * pRefTypeTag,RefTypeId * pRefTypeId)693  void 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   */
dvmDbgGetClassObjectType(RefTypeId refTypeId)710  u1 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   */
dvmDbgGetSignature(RefTypeId refTypeId)727  char* 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   */
dvmDbgGetSourceFile(RefTypeId refTypeId)742  const 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   */
dvmDbgGetObjectTypeName(ObjectId objectId)757  char* 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   */
dvmDbgGetSignatureTag(const char * type)775  int 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   */
resultTagFromObject(Object * obj)816  static 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   */
dvmDbgGetObjectTag(ObjectId objectId,const char * type)849  int 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   */
dvmDbgGetTagWidth(int tag)863  int 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   */
isTagPrimitive(u1 tag)898  static 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   */
dvmDbgGetArrayLength(ObjectId arrayId)930  int 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   */
dvmDbgGetArrayElementTag(ObjectId arrayId)940  int 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   */
copyValuesToBE(u1 * out,const u1 * in,int count,int width)953  static 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   */
copyValuesFromBE(u1 * out,const u1 * in,int count,int width)982  static 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   */
dvmDbgOutputArray(ObjectId arrayId,int firstIndex,int count,ExpandBuf * pReply)1012  bool 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   */
dvmDbgSetArrayElements(ObjectId arrayId,int firstIndex,int count,const u1 * buf)1063  bool 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   */
dvmDbgCreateString(const char * str)1110  ObjectId 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   */
dvmDbgMatchType(RefTypeId instClassId,RefTypeId classId)1122  bool 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   */
dvmDbgGetMethodName(RefTypeId refTypeId,MethodId id)1140  const 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   */
dvmDbgOutputAllFields(RefTypeId refTypeId,bool withGeneric,ExpandBuf * pReply)1153  void 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   */
dvmDbgOutputAllMethods(RefTypeId refTypeId,bool withGeneric,ExpandBuf * pReply)1195  void 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   */
dvmDbgOutputAllInterfaces(RefTypeId refTypeId,ExpandBuf * pReply)1248  void 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  
1269  typedef struct DebugCallbackContext {
1270      int numItems;
1271      ExpandBuf* pReply;
1272      // used by locals table
1273      bool withGeneric;
1274  } DebugCallbackContext;
1275  
lineTablePositionsCb(void * cnxt,u4 address,u4 lineNum)1276  static 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   */
dvmDbgOutputLineTable(RefTypeId refTypeId,MethodId methodId,ExpandBuf * pReply)1292  void 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   */
tweakSlot(int slot,const char * name)1339  static 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   */
untweakSlot(int slot,const void * framePtr)1355  static 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  
variableTableCb(void * cnxt,u2 reg,u4 startAddress,u4 endAddress,const char * name,const char * descriptor,const char * signature)1371  static 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   */
dvmDbgOutputVariableTable(RefTypeId refTypeId,MethodId methodId,bool withGeneric,ExpandBuf * pReply)1399  void 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   */
dvmDbgGetFieldTag(ObjectId objId,FieldId fieldId)1430  int 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   */
dvmDbgGetStaticFieldTag(RefTypeId refTypeId,FieldId fieldId)1442  int 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   */
dvmDbgGetFieldValue(ObjectId objectId,FieldId fieldId,u1 * buf,int expectedLen)1451  void 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   */
dvmDbgSetFieldValue(ObjectId objectId,FieldId fieldId,u8 value,int width)1506  void 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   */
dvmDbgGetStaticFieldValue(RefTypeId refTypeId,FieldId fieldId,u1 * buf,int expectedLen)1552  void 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   */
dvmDbgSetStaticFieldValue(RefTypeId refTypeId,FieldId fieldId,u8 rawValue,int width)1610  void 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   */
dvmDbgStringToUtf8(ObjectId strId)1670  char* 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   */
threadObjToThread(Object * threadObj)1692  static 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   */
dvmDbgGetThreadStatus(ObjectId threadId,u4 * pThreadStatus,u4 * pSuspendStatus)1707  bool 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  
1747  bail:
1748      dvmUnlockThreadList();
1749      return result;
1750  }
1751  
1752  /*
1753   * Get the thread's suspend count.
1754   */
dvmDbgGetThreadSuspendCount(ObjectId threadId)1755  u4 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  
1773  bail:
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   */
dvmDbgThreadExists(ObjectId threadId)1783  bool 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   */
dvmDbgIsSuspended(ObjectId threadId)1810  bool 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  
1828  bail:
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   */
1841  void 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   */
dvmDbgGetSystemThreadGroupId(void)1862  ObjectId dvmDbgGetSystemThreadGroupId(void)
1863  {
1864      Object* groupObj = dvmGetSystemThreadGroup();
1865      return objectToObjectId(groupObj);
1866  }
1867  
1868  /*
1869   * Return the ObjectId for the "system" thread group.
1870   */
dvmDbgGetMainThreadGroupId(void)1871  ObjectId 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   */
dvmDbgGetThreadName(ObjectId threadId)1882  char* 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   */
dvmDbgGetThreadGroup(ObjectId threadId)1913  ObjectId 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   */
dvmDbgGetThreadGroupName(ObjectId threadGroupId)1931  char* 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   */
dvmDbgGetThreadGroupParent(ObjectId threadGroupId)1957  ObjectId 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   */
dvmDbgGetThreadGroupThreads(ObjectId threadGroupId,ObjectId ** ppThreadIds,u4 * pThreadCount)1988  void 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   */
dvmDbgGetAllThreads(ObjectId ** ppThreadIds,u4 * pThreadCount)2074  void 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   */
dvmDbgGetThreadFrameCount(ObjectId threadId)2085  int 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  
2108  bail:
2109      dvmUnlockThreadList();
2110      return count;
2111  }
2112  
2113  /*
2114   * Get info for frame N from the specified thread's stack.
2115   */
dvmDbgGetThreadFrame(ObjectId threadId,int num,FrameId * pFrameId,JdwpLocation * pLoc)2116  bool 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  
2161  bail:
2162      dvmUnlockThreadList();
2163      return false;
2164  }
2165  
2166  /*
2167   * Get the ThreadId for the current thread.
2168   */
dvmDbgGetThreadSelfId(void)2169  ObjectId dvmDbgGetThreadSelfId(void)
2170  {
2171      Thread* self = dvmThreadSelf();
2172      return objectToObjectId(self->threadObj);
2173  }
2174  
2175  /*
2176   * Suspend the VM.
2177   */
dvmDbgSuspendVM(bool isEvent)2178  void dvmDbgSuspendVM(bool isEvent)
2179  {
2180      dvmSuspendAllThreads(isEvent ? SUSPEND_FOR_DEBUG_EVENT : SUSPEND_FOR_DEBUG);
2181  }
2182  
2183  /*
2184   * Resume the VM.
2185   */
dvmDbgResumeVM()2186  void dvmDbgResumeVM()
2187  {
2188      dvmResumeAllThreads(SUSPEND_FOR_DEBUG);
2189  }
2190  
2191  /*
2192   * Suspend one thread (not ourselves).
2193   */
dvmDbgSuspendThread(ObjectId threadId)2194  void 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   */
dvmDbgResumeThread(ObjectId threadId)2215  void 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   */
dvmDbgSuspendSelf(void)2235  void dvmDbgSuspendSelf(void)
2236  {
2237      dvmSuspendSelf(true);
2238  }
2239  
2240  /*
2241   * Get the "this" object for the specified frame.
2242   */
getThisObject(const u4 * framePtr)2243  static 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   */
dvmDbgGetThisObject(ObjectId threadId,FrameId frameId,ObjectId * pThisId)2283  bool 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   */
dvmDbgGetLocalValue(ObjectId threadId,FrameId frameId,int slot,u1 tag,u1 * buf,int expectedLen)2300  void 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   */
dvmDbgSetLocalValue(ObjectId threadId,FrameId frameId,int slot,u1 tag,u8 value,int width)2391  void 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   */
dvmDbgPostLocationEvent(const Method * method,int pcOffset,Object * thisPtr,int eventFlags)2458  void 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   */
dvmDbgPostException(void * throwFp,int throwRelPc,void * catchFp,int catchRelPc,Object * exception)2488  void 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   */
dvmDbgPostThreadStart(Thread * thread)2528  void 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   */
dvmDbgPostThreadDeath(Thread * thread)2541  void 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   */
dvmDbgPostClassPrepare(ClassObject * clazz)2554  void 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   */
dvmDbgWatchLocation(const JdwpLocation * pLoc)2578  bool 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   */
dvmDbgUnwatchLocation(const JdwpLocation * pLoc)2589  void 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   */
dvmDbgConfigureStep(ObjectId threadId,enum JdwpStepSize size,enum JdwpStepDepth depth)2600  bool 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  
2634  bail:
2635      dvmUnlockThreadList();
2636      return result;
2637  }
2638  
2639  /*
2640   * A single-step event has been removed.
2641   */
dvmDbgUnconfigureStep(ObjectId threadId)2642  void 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   */
dvmDbgInvokeMethod(ObjectId threadId,ObjectId objectId,RefTypeId classId,MethodId methodId,u4 numArgs,ObjectId * argArray,u4 options,u1 * pResultTag,u8 * pResultValue,ObjectId * pExceptObj)2656  JdwpError 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      /*
2700       * 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.
2704       */
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  
2762  bail:
2763      return err;
2764  }
2765  
2766  /*
2767   * Determine the tag type for the return value for this method.
2768   */
resultTagFromSignature(const Method * method)2769  static 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".
2777   *
2778   * We're currently in VMWAIT, because we're stopped on a breakpoint.  We
2779   * want to switch to RUNNING while we execute.
2780   */
dvmDbgExecuteMethod(DebugInvokeReq * pReq)2781  void dvmDbgExecuteMethod(DebugInvokeReq* pReq)
2782  {
2783      Thread* self = dvmThreadSelf();
2784      const Method* meth;
2785      Object* oldExcept;
2786      int oldStatus;
2787  
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);
2793      dvmClearException(self);
2794  
2795      oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
2796  
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, false, &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);
2842      dvmChangeStatus(self, oldStatus);
2843  }
2844  
2845  // for dvmAddressSetForLine
2846  typedef struct AddressSetContext {
2847      bool lastAddressValid;
2848      u4 lastAddress;
2849      u4 lineNum;
2850      AddressSet *pSet;
2851  } AddressSetContext;
2852  
2853  // for dvmAddressSetForLine
addressSetCb(void * cnxt,u4 address,u4 lineNum)2854  static 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   */
dvmAddressSetForLine(const Method * method,int line)2882  const 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   */
dvmDbgDdmHandlePacket(const u1 * buf,int dataLen,u1 ** pReplyBuf,int * pReplyLen)2924  bool 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   */
dvmDbgDdmConnected(void)2933  void dvmDbgDdmConnected(void)
2934  {
2935      dvmDdmConnected();
2936  }
2937  
2938  /*
2939   * JDWP connection has dropped.
2940   */
dvmDbgDdmDisconnected(void)2941  void dvmDbgDdmDisconnected(void)
2942  {
2943      dvmDdmDisconnected();
2944  }
2945  
2946  /*
2947   * Send up a JDWP event packet with a DDM chunk in it.
2948   */
dvmDbgDdmSendChunk(int type,int len,const u1 * buf)2949  void 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  
2960