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