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