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