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