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