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 * Exception handling. 18 */ 19 #include "Dalvik.h" 20 #include "libdex/DexCatch.h" 21 22 #include <stdlib.h> 23 24 /* 25 Notes on Exception Handling 26 27 We have one fairly sticky issue to deal with: creating the exception stack 28 trace. The trouble is that we need the current value of the program 29 counter for the method now being executed, but that's only held in a local 30 variable or hardware register in the main interpreter loop. 31 32 The exception mechanism requires that the current stack trace be associated 33 with a Throwable at the time the Throwable is constructed. The construction 34 may or may not be associated with a throw. We have three situations to 35 consider: 36 37 (1) A Throwable is created with a "new Throwable" statement in the 38 application code, for immediate or deferred use with a "throw" statement. 39 (2) The VM throws an exception from within the interpreter core, e.g. 40 after an integer divide-by-zero. 41 (3) The VM throws an exception from somewhere deeper down, e.g. while 42 trying to link a class. 43 44 We need to have the current value for the PC, which means that for 45 situation (3) the interpreter loop must copy it to an externally-accessible 46 location before handling any opcode that could cause the VM to throw 47 an exception. We can't store it globally, because the various threads 48 would trample each other. We can't store it in the Thread structure, 49 because it'll get overwritten as soon as the Throwable constructor starts 50 executing. It needs to go on the stack, but our stack frames hold the 51 caller's *saved* PC, not the current PC. 52 53 Situation #1 doesn't require special handling. Situation #2 could be dealt 54 with by passing the PC into the exception creation function. The trick 55 is to solve situation #3 in a way that adds minimal overhead to common 56 operations. Making it more costly to throw an exception is acceptable. 57 58 There are a few ways to deal with this: 59 60 (a) Change "savedPc" to "currentPc" in the stack frame. All of the 61 stack logic gets offset by one frame. The current PC is written 62 to the current stack frame when necessary. 63 (b) Write the current PC into the current stack frame, but without 64 replacing "savedPc". The JNI local refs pointer, which is only 65 used for native code, can be overloaded to save space. 66 (c) In dvmThrowException(), push an extra stack frame on, with the 67 current PC in it. The current PC is written into the Thread struct 68 when necessary, and copied out when the VM throws. 69 (d) Before doing something that might throw an exception, push a 70 temporary frame on with the saved PC in it. 71 72 Solution (a) is the simplest, but breaks Dalvik's goal of mingling native 73 and interpreted stacks. 74 75 Solution (b) retains the simplicity of (a) without rearranging the stack, 76 but now in some cases we're storing the PC twice, which feels wrong. 77 78 Solution (c) usually works, because we push the saved PC onto the stack 79 before the Throwable construction can overwrite the copy in Thread. One 80 way solution (c) could break is: 81 - Interpreter saves the PC 82 - Execute some bytecode, which runs successfully (and alters the saved PC) 83 - Throw an exception before re-saving the PC (i.e in the same opcode) 84 This is a risk for anything that could cause <clinit> to execute, e.g. 85 executing a static method or accessing a static field. Attemping to access 86 a field that doesn't exist in a class that does exist might cause this. 87 It may be possible to simply bracket the dvmCallMethod*() functions to 88 save/restore it. 89 90 Solution (d) incurs additional overhead, but may have other benefits (e.g. 91 it's easy to find the stack frames that should be removed before storage 92 in the Throwable). 93 94 Current plan is option (b), because it's simple, fast, and doesn't change 95 the way the stack works. 96 */ 97 98 /* fwd */ 99 static bool initException(Object* exception, const char* msg, Object* cause, 100 Thread* self); 101 102 103 /* 104 * Cache pointers to some of the exception classes we use locally. 105 * 106 * Note this is NOT called during dexopt optimization. Some of the fields 107 * are initialized by the verifier (dvmVerifyCodeFlow). 108 */ 109 bool dvmExceptionStartup(void) 110 { 111 gDvm.classJavaLangThrowable = 112 dvmFindSystemClassNoInit("Ljava/lang/Throwable;"); 113 gDvm.classJavaLangRuntimeException = 114 dvmFindSystemClassNoInit("Ljava/lang/RuntimeException;"); 115 gDvm.classJavaLangStackOverflowError = 116 dvmFindSystemClassNoInit("Ljava/lang/StackOverflowError;"); 117 gDvm.classJavaLangError = 118 dvmFindSystemClassNoInit("Ljava/lang/Error;"); 119 gDvm.classJavaLangStackTraceElement = 120 dvmFindSystemClassNoInit("Ljava/lang/StackTraceElement;"); 121 gDvm.classJavaLangStackTraceElementArray = 122 dvmFindArrayClass("[Ljava/lang/StackTraceElement;", NULL); 123 if (gDvm.classJavaLangThrowable == NULL || 124 gDvm.classJavaLangStackTraceElement == NULL || 125 gDvm.classJavaLangStackTraceElementArray == NULL) 126 { 127 LOGE("Could not find one or more essential exception classes\n"); 128 return false; 129 } 130 131 /* 132 * Find the constructor. Note that, unlike other saved method lookups, 133 * we're using a Method* instead of a vtable offset. This is because 134 * constructors don't have vtable offsets. (Also, since we're creating 135 * the object in question, it's impossible for anyone to sub-class it.) 136 */ 137 Method* meth; 138 meth = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangStackTraceElement, 139 "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V"); 140 if (meth == NULL) { 141 LOGE("Unable to find constructor for StackTraceElement\n"); 142 return false; 143 } 144 gDvm.methJavaLangStackTraceElement_init = meth; 145 146 /* grab an offset for the stackData field */ 147 gDvm.offJavaLangThrowable_stackState = 148 dvmFindFieldOffset(gDvm.classJavaLangThrowable, 149 "stackState", "Ljava/lang/Object;"); 150 if (gDvm.offJavaLangThrowable_stackState < 0) { 151 LOGE("Unable to find Throwable.stackState\n"); 152 return false; 153 } 154 155 /* and one for the message field, in case we want to show it */ 156 gDvm.offJavaLangThrowable_message = 157 dvmFindFieldOffset(gDvm.classJavaLangThrowable, 158 "detailMessage", "Ljava/lang/String;"); 159 if (gDvm.offJavaLangThrowable_message < 0) { 160 LOGE("Unable to find Throwable.detailMessage\n"); 161 return false; 162 } 163 164 /* and one for the cause field, just 'cause */ 165 gDvm.offJavaLangThrowable_cause = 166 dvmFindFieldOffset(gDvm.classJavaLangThrowable, 167 "cause", "Ljava/lang/Throwable;"); 168 if (gDvm.offJavaLangThrowable_cause < 0) { 169 LOGE("Unable to find Throwable.cause\n"); 170 return false; 171 } 172 173 return true; 174 } 175 176 /* 177 * Clean up. 178 */ 179 void dvmExceptionShutdown(void) 180 { 181 // nothing to do 182 } 183 184 185 /* 186 * Format the message into a small buffer and pass it along. 187 */ 188 void dvmThrowExceptionFmtV(const char* exceptionDescriptor, const char* fmt, 189 va_list args) 190 { 191 char msgBuf[512]; 192 193 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); 194 dvmThrowChainedException(exceptionDescriptor, msgBuf, NULL); 195 } 196 197 /* 198 * Create a Throwable and throw an exception in the current thread (where 199 * "throwing" just means "set the thread's exception pointer"). 200 * 201 * "msg" and/or "cause" may be NULL. 202 * 203 * If we have a bad exception hierarchy -- something in Throwable.<init> 204 * is missing -- then every attempt to throw an exception will result 205 * in another exception. Exceptions are generally allowed to "chain" 206 * to other exceptions, so it's hard to auto-detect this problem. It can 207 * only happen if the system classes are broken, so it's probably not 208 * worth spending cycles to detect it. 209 * 210 * We do have one case to worry about: if the classpath is completely 211 * wrong, we'll go into a death spin during startup because we can't find 212 * the initial class and then we can't find NoClassDefFoundError. We have 213 * to handle this case. 214 * 215 * [Do we want to cache pointers to common exception classes?] 216 */ 217 void dvmThrowChainedException(const char* exceptionDescriptor, const char* msg, 218 Object* cause) 219 { 220 ClassObject* excepClass; 221 222 LOGV("THROW '%s' msg='%s' cause=%s\n", 223 exceptionDescriptor, msg, 224 (cause != NULL) ? cause->clazz->descriptor : "(none)"); 225 226 if (gDvm.initializing) { 227 if (++gDvm.initExceptionCount >= 2) { 228 LOGE("Too many exceptions during init (failed on '%s' '%s')\n", 229 exceptionDescriptor, msg); 230 dvmAbort(); 231 } 232 } 233 234 excepClass = dvmFindSystemClass(exceptionDescriptor); 235 if (excepClass == NULL) { 236 /* 237 * We couldn't find the exception class. The attempt to find a 238 * nonexistent class should have raised an exception. If no 239 * exception is currently raised, then we're pretty clearly unable 240 * to throw ANY sort of exception, and we need to pack it in. 241 * 242 * If we were able to throw the "class load failed" exception, 243 * stick with that. Ideally we'd stuff the original exception 244 * into the "cause" field, but since we can't find it we can't 245 * do that. The exception class name should be in the "message" 246 * field. 247 */ 248 if (!dvmCheckException(dvmThreadSelf())) { 249 LOGE("FATAL: unable to throw exception (failed on '%s' '%s')\n", 250 exceptionDescriptor, msg); 251 dvmAbort(); 252 } 253 return; 254 } 255 256 dvmThrowChainedExceptionByClass(excepClass, msg, cause); 257 } 258 259 /* 260 * Start/continue throwing process now that we have a class reference. 261 */ 262 void dvmThrowChainedExceptionByClass(ClassObject* excepClass, const char* msg, 263 Object* cause) 264 { 265 Thread* self = dvmThreadSelf(); 266 Object* exception; 267 268 /* make sure the exception is initialized */ 269 if (!dvmIsClassInitialized(excepClass) && !dvmInitClass(excepClass)) { 270 LOGE("ERROR: unable to initialize exception class '%s'\n", 271 excepClass->descriptor); 272 if (strcmp(excepClass->descriptor, "Ljava/lang/InternalError;") == 0) 273 dvmAbort(); 274 dvmThrowChainedException("Ljava/lang/InternalError;", 275 "failed to init original exception class", cause); 276 return; 277 } 278 279 exception = dvmAllocObject(excepClass, ALLOC_DEFAULT); 280 if (exception == NULL) { 281 /* 282 * We're in a lot of trouble. We might be in the process of 283 * throwing an out-of-memory exception, in which case the 284 * pre-allocated object will have been thrown when our object alloc 285 * failed. So long as there's an exception raised, return and 286 * allow the system to try to recover. If not, something is broken 287 * and we need to bail out. 288 */ 289 if (dvmCheckException(self)) 290 goto bail; 291 LOGE("FATAL: unable to allocate exception '%s' '%s'\n", 292 excepClass->descriptor, msg != NULL ? msg : "(no msg)"); 293 dvmAbort(); 294 } 295 296 /* 297 * Init the exception. 298 */ 299 if (gDvm.optimizing) { 300 /* need the exception object, but can't invoke interpreted code */ 301 LOGV("Skipping init of exception %s '%s'\n", 302 excepClass->descriptor, msg); 303 } else { 304 assert(excepClass == exception->clazz); 305 if (!initException(exception, msg, cause, self)) { 306 /* 307 * Whoops. If we can't initialize the exception, we can't use 308 * it. If there's an exception already set, the constructor 309 * probably threw an OutOfMemoryError. 310 */ 311 if (!dvmCheckException(self)) { 312 /* 313 * We're required to throw something, so we just 314 * throw the pre-constructed internal error. 315 */ 316 self->exception = gDvm.internalErrorObj; 317 } 318 goto bail; 319 } 320 } 321 322 self->exception = exception; 323 324 bail: 325 dvmReleaseTrackedAlloc(exception, self); 326 } 327 328 /* 329 * Throw the named exception using the dotted form of the class 330 * descriptor as the exception message, and with the specified cause. 331 */ 332 void dvmThrowChainedExceptionWithClassMessage(const char* exceptionDescriptor, 333 const char* messageDescriptor, Object* cause) 334 { 335 char* message = dvmDescriptorToDot(messageDescriptor); 336 337 dvmThrowChainedException(exceptionDescriptor, message, cause); 338 free(message); 339 } 340 341 /* 342 * Like dvmThrowExceptionWithMessageFromDescriptor, but take a 343 * class object instead of a name. 344 */ 345 void dvmThrowExceptionByClassWithClassMessage(ClassObject* exceptionClass, 346 const char* messageDescriptor) 347 { 348 char* message = dvmDescriptorToName(messageDescriptor); 349 350 dvmThrowExceptionByClass(exceptionClass, message); 351 free(message); 352 } 353 354 /* 355 * Find and return an exception constructor method that can take the 356 * indicated parameters, or return NULL if no such constructor exists. 357 */ 358 static Method* findExceptionInitMethod(ClassObject* excepClass, 359 bool hasMessage, bool hasCause) 360 { 361 if (hasMessage) { 362 Method* result; 363 364 if (hasCause) { 365 result = dvmFindDirectMethodByDescriptor( 366 excepClass, "<init>", 367 "(Ljava/lang/String;Ljava/lang/Throwable;)V"); 368 } else { 369 result = dvmFindDirectMethodByDescriptor( 370 excepClass, "<init>", "(Ljava/lang/String;)V"); 371 } 372 373 if (result != NULL) { 374 return result; 375 } 376 377 if (hasCause) { 378 return dvmFindDirectMethodByDescriptor( 379 excepClass, "<init>", 380 "(Ljava/lang/Object;Ljava/lang/Throwable;)V"); 381 } else { 382 return dvmFindDirectMethodByDescriptor( 383 excepClass, "<init>", "(Ljava/lang/Object;)V"); 384 } 385 } else if (hasCause) { 386 return dvmFindDirectMethodByDescriptor( 387 excepClass, "<init>", "(Ljava/lang/Throwable;)V"); 388 } else { 389 return dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V"); 390 } 391 } 392 393 /* 394 * Initialize an exception with an appropriate constructor. 395 * 396 * "exception" is the exception object to initialize. 397 * Either or both of "msg" and "cause" may be null. 398 * "self" is dvmThreadSelf(), passed in so we don't have to look it up again. 399 * 400 * If the process of initializing the exception causes another 401 * exception (e.g., OutOfMemoryError) to be thrown, return an error 402 * and leave self->exception intact. 403 */ 404 static bool initException(Object* exception, const char* msg, Object* cause, 405 Thread* self) 406 { 407 enum { 408 kInitUnknown, 409 kInitNoarg, 410 kInitMsg, 411 kInitMsgThrow, 412 kInitThrow 413 } initKind = kInitUnknown; 414 Method* initMethod = NULL; 415 ClassObject* excepClass = exception->clazz; 416 StringObject* msgStr = NULL; 417 bool result = false; 418 bool needInitCause = false; 419 420 assert(self != NULL); 421 assert(self->exception == NULL); 422 423 /* if we have a message, create a String */ 424 if (msg == NULL) 425 msgStr = NULL; 426 else { 427 msgStr = dvmCreateStringFromCstr(msg, ALLOC_DEFAULT); 428 if (msgStr == NULL) { 429 LOGW("Could not allocate message string \"%s\" while " 430 "throwing internal exception (%s)\n", 431 msg, excepClass->descriptor); 432 goto bail; 433 } 434 } 435 436 if (cause != NULL) { 437 if (!dvmInstanceof(cause->clazz, gDvm.classJavaLangThrowable)) { 438 LOGE("Tried to init exception with cause '%s'\n", 439 cause->clazz->descriptor); 440 dvmAbort(); 441 } 442 } 443 444 /* 445 * The Throwable class has four public constructors: 446 * (1) Throwable() 447 * (2) Throwable(String message) 448 * (3) Throwable(String message, Throwable cause) (added in 1.4) 449 * (4) Throwable(Throwable cause) (added in 1.4) 450 * 451 * The first two are part of the original design, and most exception 452 * classes should support them. The third prototype was used by 453 * individual exceptions. e.g. ClassNotFoundException added it in 1.2. 454 * The general "cause" mechanism was added in 1.4. Some classes, 455 * such as IllegalArgumentException, initially supported the first 456 * two, but added the second two in a later release. 457 * 458 * Exceptions may be picky about how their "cause" field is initialized. 459 * If you call ClassNotFoundException(String), it may choose to 460 * initialize its "cause" field to null. Doing so prevents future 461 * calls to Throwable.initCause(). 462 * 463 * So, if "cause" is not NULL, we need to look for a constructor that 464 * takes a throwable. If we can't find one, we fall back on calling 465 * #1/#2 and making a separate call to initCause(). Passing a null ref 466 * for "message" into Throwable(String, Throwable) is allowed, but we 467 * prefer to use the Throwable-only version because it has different 468 * behavior. 469 * 470 * java.lang.TypeNotPresentException is a strange case -- it has #3 but 471 * not #2. (Some might argue that the constructor is actually not #3, 472 * because it doesn't take the message string as an argument, but it 473 * has the same effect and we can work with it here.) 474 * 475 * java.lang.AssertionError is also a strange case -- it has a 476 * constructor that takes an Object, but not one that takes a String. 477 * There may be other cases like this, as well, so we generally look 478 * for an Object-taking constructor if we can't find one that takes 479 * a String. 480 */ 481 if (cause == NULL) { 482 if (msgStr == NULL) { 483 initMethod = findExceptionInitMethod(excepClass, false, false); 484 initKind = kInitNoarg; 485 } else { 486 initMethod = findExceptionInitMethod(excepClass, true, false); 487 if (initMethod != NULL) { 488 initKind = kInitMsg; 489 } else { 490 /* no #2, try #3 */ 491 initMethod = findExceptionInitMethod(excepClass, true, true); 492 if (initMethod != NULL) { 493 initKind = kInitMsgThrow; 494 } 495 } 496 } 497 } else { 498 if (msgStr == NULL) { 499 initMethod = findExceptionInitMethod(excepClass, false, true); 500 if (initMethod != NULL) { 501 initKind = kInitThrow; 502 } else { 503 initMethod = findExceptionInitMethod(excepClass, false, false); 504 initKind = kInitNoarg; 505 needInitCause = true; 506 } 507 } else { 508 initMethod = findExceptionInitMethod(excepClass, true, true); 509 if (initMethod != NULL) { 510 initKind = kInitMsgThrow; 511 } else { 512 initMethod = findExceptionInitMethod(excepClass, true, false); 513 initKind = kInitMsg; 514 needInitCause = true; 515 } 516 } 517 } 518 519 if (initMethod == NULL) { 520 /* 521 * We can't find the desired constructor. This can happen if a 522 * subclass of java/lang/Throwable doesn't define an expected 523 * constructor, e.g. it doesn't provide one that takes a string 524 * when a message has been provided. 525 */ 526 LOGW("WARNING: exception class '%s' missing constructor " 527 "(msg='%s' kind=%d)\n", 528 excepClass->descriptor, msg, initKind); 529 assert(strcmp(excepClass->descriptor, 530 "Ljava/lang/RuntimeException;") != 0); 531 dvmThrowChainedException("Ljava/lang/RuntimeException;", 532 "re-throw on exception class missing constructor", NULL); 533 goto bail; 534 } 535 536 /* 537 * Call the constructor with the appropriate arguments. 538 */ 539 JValue unused; 540 switch (initKind) { 541 case kInitNoarg: 542 LOGVV("+++ exc noarg (ic=%d)\n", needInitCause); 543 dvmCallMethod(self, initMethod, exception, &unused); 544 break; 545 case kInitMsg: 546 LOGVV("+++ exc msg (ic=%d)\n", needInitCause); 547 dvmCallMethod(self, initMethod, exception, &unused, msgStr); 548 break; 549 case kInitThrow: 550 LOGVV("+++ exc throw"); 551 assert(!needInitCause); 552 dvmCallMethod(self, initMethod, exception, &unused, cause); 553 break; 554 case kInitMsgThrow: 555 LOGVV("+++ exc msg+throw"); 556 assert(!needInitCause); 557 dvmCallMethod(self, initMethod, exception, &unused, msgStr, cause); 558 break; 559 default: 560 assert(false); 561 goto bail; 562 } 563 564 /* 565 * It's possible the constructor has thrown an exception. If so, we 566 * return an error and let our caller deal with it. 567 */ 568 if (self->exception != NULL) { 569 LOGW("Exception thrown (%s) while throwing internal exception (%s)\n", 570 self->exception->clazz->descriptor, exception->clazz->descriptor); 571 goto bail; 572 } 573 574 /* 575 * If this exception was caused by another exception, and we weren't 576 * able to find a cause-setting constructor, set the "cause" field 577 * with an explicit call. 578 */ 579 if (needInitCause) { 580 Method* initCause; 581 initCause = dvmFindVirtualMethodHierByDescriptor(excepClass, "initCause", 582 "(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); 583 if (initCause != NULL) { 584 dvmCallMethod(self, initCause, exception, &unused, cause); 585 if (self->exception != NULL) { 586 /* initCause() threw an exception; return an error and 587 * let the caller deal with it. 588 */ 589 LOGW("Exception thrown (%s) during initCause() " 590 "of internal exception (%s)\n", 591 self->exception->clazz->descriptor, 592 exception->clazz->descriptor); 593 goto bail; 594 } 595 } else { 596 LOGW("WARNING: couldn't find initCause in '%s'\n", 597 excepClass->descriptor); 598 } 599 } 600 601 602 result = true; 603 604 bail: 605 dvmReleaseTrackedAlloc((Object*) msgStr, self); // NULL is ok 606 return result; 607 } 608 609 610 /* 611 * Clear the pending exception and the "initExceptionCount" counter. This 612 * is used by the optimization and verification code, which has to run with 613 * "initializing" set to avoid going into a death-spin if the "class not 614 * found" exception can't be found. 615 * 616 * This can also be called when the VM is in a "normal" state, e.g. when 617 * verifying classes that couldn't be verified at optimization time. The 618 * reset of initExceptionCount should be harmless in that case. 619 */ 620 void dvmClearOptException(Thread* self) 621 { 622 self->exception = NULL; 623 gDvm.initExceptionCount = 0; 624 } 625 626 /* 627 * Returns "true" if this is a "checked" exception, i.e. it's a subclass 628 * of Throwable (assumed) but not a subclass of RuntimeException or Error. 629 */ 630 bool dvmIsCheckedException(const Object* exception) 631 { 632 if (dvmInstanceof(exception->clazz, gDvm.classJavaLangError) || 633 dvmInstanceof(exception->clazz, gDvm.classJavaLangRuntimeException)) 634 { 635 return false; 636 } else { 637 return true; 638 } 639 } 640 641 /* 642 * Wrap the now-pending exception in a different exception. This is useful 643 * for reflection stuff that wants to hand a checked exception back from a 644 * method that doesn't declare it. 645 * 646 * If something fails, an (unchecked) exception related to that failure 647 * will be pending instead. 648 */ 649 void dvmWrapException(const char* newExcepStr) 650 { 651 Thread* self = dvmThreadSelf(); 652 Object* origExcep; 653 ClassObject* iteClass; 654 655 origExcep = dvmGetException(self); 656 dvmAddTrackedAlloc(origExcep, self); // don't let the GC free it 657 658 dvmClearException(self); // clear before class lookup 659 iteClass = dvmFindSystemClass(newExcepStr); 660 if (iteClass != NULL) { 661 Object* iteExcep; 662 Method* initMethod; 663 664 iteExcep = dvmAllocObject(iteClass, ALLOC_DEFAULT); 665 if (iteExcep != NULL) { 666 initMethod = dvmFindDirectMethodByDescriptor(iteClass, "<init>", 667 "(Ljava/lang/Throwable;)V"); 668 if (initMethod != NULL) { 669 JValue unused; 670 dvmCallMethod(self, initMethod, iteExcep, &unused, 671 origExcep); 672 673 /* if <init> succeeded, replace the old exception */ 674 if (!dvmCheckException(self)) 675 dvmSetException(self, iteExcep); 676 } 677 dvmReleaseTrackedAlloc(iteExcep, NULL); 678 679 /* if initMethod doesn't exist, or failed... */ 680 if (!dvmCheckException(self)) 681 dvmSetException(self, origExcep); 682 } else { 683 /* leave OutOfMemoryError pending */ 684 } 685 } else { 686 /* leave ClassNotFoundException pending */ 687 } 688 689 assert(dvmCheckException(self)); 690 dvmReleaseTrackedAlloc(origExcep, self); 691 } 692 693 /* 694 * Get the "cause" field from an exception. 695 * 696 * The Throwable class initializes the "cause" field to "this" to 697 * differentiate between being initialized to null and never being 698 * initialized. We check for that here and convert it to NULL. 699 */ 700 Object* dvmGetExceptionCause(const Object* exception) 701 { 702 if (!dvmInstanceof(exception->clazz, gDvm.classJavaLangThrowable)) { 703 LOGE("Tried to get cause from object of type '%s'\n", 704 exception->clazz->descriptor); 705 dvmAbort(); 706 } 707 Object* cause = 708 dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause); 709 if (cause == exception) 710 return NULL; 711 else 712 return cause; 713 } 714 715 /* 716 * Print the stack trace of the current exception on stderr. This is called 717 * from the JNI ExceptionDescribe call. 718 * 719 * For consistency we just invoke the Throwable printStackTrace method, 720 * which might be overridden in the exception object. 721 * 722 * Exceptions thrown during the course of printing the stack trace are 723 * ignored. 724 */ 725 void dvmPrintExceptionStackTrace(void) 726 { 727 Thread* self = dvmThreadSelf(); 728 Object* exception; 729 Method* printMethod; 730 731 exception = self->exception; 732 if (exception == NULL) 733 return; 734 735 self->exception = NULL; 736 printMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz, 737 "printStackTrace", "()V"); 738 if (printMethod != NULL) { 739 JValue unused; 740 dvmCallMethod(self, printMethod, exception, &unused); 741 } else { 742 LOGW("WARNING: could not find printStackTrace in %s\n", 743 exception->clazz->descriptor); 744 } 745 746 if (self->exception != NULL) { 747 LOGI("NOTE: exception thrown while printing stack trace: %s\n", 748 self->exception->clazz->descriptor); 749 } 750 751 self->exception = exception; 752 } 753 754 /* 755 * Search the method's list of exceptions for a match. 756 * 757 * Returns the offset of the catch block on success, or -1 on failure. 758 */ 759 static int findCatchInMethod(Thread* self, const Method* method, int relPc, 760 ClassObject* excepClass) 761 { 762 /* 763 * Need to clear the exception before entry. Otherwise, dvmResolveClass 764 * might think somebody threw an exception while it was loading a class. 765 */ 766 assert(!dvmCheckException(self)); 767 assert(!dvmIsNativeMethod(method)); 768 769 LOGVV("findCatchInMethod %s.%s excep=%s depth=%d\n", 770 method->clazz->descriptor, method->name, excepClass->descriptor, 771 dvmComputeExactFrameDepth(self->curFrame)); 772 773 DvmDex* pDvmDex = method->clazz->pDvmDex; 774 const DexCode* pCode = dvmGetMethodCode(method); 775 DexCatchIterator iterator; 776 777 if (dexFindCatchHandler(&iterator, pCode, relPc)) { 778 for (;;) { 779 DexCatchHandler* handler = dexCatchIteratorNext(&iterator); 780 781 if (handler == NULL) { 782 break; 783 } 784 785 if (handler->typeIdx == kDexNoIndex) { 786 /* catch-all */ 787 LOGV("Match on catch-all block at 0x%02x in %s.%s for %s\n", 788 relPc, method->clazz->descriptor, 789 method->name, excepClass->descriptor); 790 return handler->address; 791 } 792 793 ClassObject* throwable = 794 dvmDexGetResolvedClass(pDvmDex, handler->typeIdx); 795 if (throwable == NULL) { 796 /* 797 * TODO: this behaves badly if we run off the stack 798 * while trying to throw an exception. The problem is 799 * that, if we're in a class loaded by a class loader, 800 * the call to dvmResolveClass has to ask the class 801 * loader for help resolving any previously-unresolved 802 * classes. If this particular class loader hasn't 803 * resolved StackOverflowError, it will call into 804 * interpreted code, and blow up. 805 * 806 * We currently replace the previous exception with 807 * the StackOverflowError, which means they won't be 808 * catching it *unless* they explicitly catch 809 * StackOverflowError, in which case we'll be unable 810 * to resolve the class referred to by the "catch" 811 * block. 812 * 813 * We end up getting a huge pile of warnings if we do 814 * a simple synthetic test, because this method gets 815 * called on every stack frame up the tree, and it 816 * fails every time. 817 * 818 * This eventually bails out, effectively becoming an 819 * uncatchable exception, so other than the flurry of 820 * warnings it's not really a problem. Still, we could 821 * probably handle this better. 822 */ 823 throwable = dvmResolveClass(method->clazz, handler->typeIdx, 824 true); 825 if (throwable == NULL) { 826 /* 827 * We couldn't find the exception they wanted in 828 * our class files (or, perhaps, the stack blew up 829 * while we were querying a class loader). Cough 830 * up a warning, then move on to the next entry. 831 * Keep the exception status clear. 832 */ 833 LOGW("Could not resolve class ref'ed in exception " 834 "catch list (class index %d, exception %s)\n", 835 handler->typeIdx, 836 (self->exception != NULL) ? 837 self->exception->clazz->descriptor : "(none)"); 838 dvmClearException(self); 839 continue; 840 } 841 } 842 843 //LOGD("ADDR MATCH, check %s instanceof %s\n", 844 // excepClass->descriptor, pEntry->excepClass->descriptor); 845 846 if (dvmInstanceof(excepClass, throwable)) { 847 LOGV("Match on catch block at 0x%02x in %s.%s for %s\n", 848 relPc, method->clazz->descriptor, 849 method->name, excepClass->descriptor); 850 return handler->address; 851 } 852 } 853 } 854 855 LOGV("No matching catch block at 0x%02x in %s for %s\n", 856 relPc, method->name, excepClass->descriptor); 857 return -1; 858 } 859 860 /* 861 * Find a matching "catch" block. "pc" is the relative PC within the 862 * current method, indicating the offset from the start in 16-bit units. 863 * 864 * Returns the offset to the catch block, or -1 if we run up against a 865 * break frame without finding anything. 866 * 867 * The class resolution stuff we have to do while evaluating the "catch" 868 * blocks could cause an exception. The caller should clear the exception 869 * before calling here and restore it after. 870 * 871 * Sets *newFrame to the frame pointer of the frame with the catch block. 872 * If "scanOnly" is false, self->curFrame is also set to this value. 873 */ 874 int dvmFindCatchBlock(Thread* self, int relPc, Object* exception, 875 bool scanOnly, void** newFrame) 876 { 877 void* fp = self->curFrame; 878 int catchAddr = -1; 879 880 assert(!dvmCheckException(self)); 881 882 while (true) { 883 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 884 catchAddr = findCatchInMethod(self, saveArea->method, relPc, 885 exception->clazz); 886 if (catchAddr >= 0) 887 break; 888 889 /* 890 * Normally we'd check for ACC_SYNCHRONIZED methods and unlock 891 * them as we unroll. Dalvik uses what amount to generated 892 * "finally" blocks to take care of this for us. 893 */ 894 895 /* output method profiling info */ 896 if (!scanOnly) { 897 TRACE_METHOD_UNROLL(self, saveArea->method); 898 } 899 900 /* 901 * Move up one frame. If the next thing up is a break frame, 902 * break out now so we're left unrolled to the last method frame. 903 * We need to point there so we can roll up the JNI local refs 904 * if this was a native method. 905 */ 906 assert(saveArea->prevFrame != NULL); 907 if (dvmIsBreakFrame(saveArea->prevFrame)) { 908 if (!scanOnly) 909 break; // bail with catchAddr == -1 910 911 /* 912 * We're scanning for the debugger. It needs to know if this 913 * exception is going to be caught or not, and we need to figure 914 * out if it will be caught *ever* not just between the current 915 * position and the next break frame. We can't tell what native 916 * code is going to do, so we assume it never catches exceptions. 917 * 918 * Start by finding an interpreted code frame. 919 */ 920 fp = saveArea->prevFrame; // this is the break frame 921 saveArea = SAVEAREA_FROM_FP(fp); 922 fp = saveArea->prevFrame; // this may be a good one 923 while (fp != NULL) { 924 if (!dvmIsBreakFrame(fp)) { 925 saveArea = SAVEAREA_FROM_FP(fp); 926 if (!dvmIsNativeMethod(saveArea->method)) 927 break; 928 } 929 930 fp = SAVEAREA_FROM_FP(fp)->prevFrame; 931 } 932 if (fp == NULL) 933 break; // bail with catchAddr == -1 934 935 /* 936 * Now fp points to the "good" frame. When the interp code 937 * invoked the native code, it saved a copy of its current PC 938 * into xtra.currentPc. Pull it out of there. 939 */ 940 relPc = 941 saveArea->xtra.currentPc - SAVEAREA_FROM_FP(fp)->method->insns; 942 } else { 943 fp = saveArea->prevFrame; 944 945 /* savedPc in was-current frame goes with method in now-current */ 946 relPc = saveArea->savedPc - SAVEAREA_FROM_FP(fp)->method->insns; 947 } 948 } 949 950 if (!scanOnly) 951 self->curFrame = fp; 952 953 /* 954 * The class resolution in findCatchInMethod() could cause an exception. 955 * Clear it to be safe. 956 */ 957 self->exception = NULL; 958 959 *newFrame = fp; 960 return catchAddr; 961 } 962 963 /* 964 * We have to carry the exception's stack trace around, but in many cases 965 * it will never be examined. It makes sense to keep it in a compact, 966 * VM-specific object, rather than an array of Objects with strings. 967 * 968 * Pass in the thread whose stack we're interested in. If "thread" is 969 * not self, the thread must be suspended. This implies that the thread 970 * list lock is held, which means we can't allocate objects or we risk 971 * jamming the GC. So, we allow this function to return different formats. 972 * (This shouldn't be called directly -- see the inline functions in the 973 * header file.) 974 * 975 * If "wantObject" is true, this returns a newly-allocated Object, which is 976 * presently an array of integers, but could become something else in the 977 * future. If "wantObject" is false, return plain malloc data. 978 * 979 * NOTE: if we support class unloading, we will need to scan the class 980 * object references out of these arrays. 981 */ 982 void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, int* pCount) 983 { 984 ArrayObject* stackData = NULL; 985 int* simpleData = NULL; 986 void* fp; 987 void* startFp; 988 int stackDepth; 989 int* intPtr; 990 991 if (pCount != NULL) 992 *pCount = 0; 993 fp = thread->curFrame; 994 995 assert(thread == dvmThreadSelf() || dvmIsSuspended(thread)); 996 997 /* 998 * We're looking at a stack frame for code running below a Throwable 999 * constructor. We want to remove the Throwable methods and the 1000 * superclass initializations so the user doesn't see them when they 1001 * read the stack dump. 1002 * 1003 * TODO: this just scrapes off the top layers of Throwable. Might not do 1004 * the right thing if we create an exception object or cause a VM 1005 * exception while in a Throwable method. 1006 */ 1007 while (fp != NULL) { 1008 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 1009 const Method* method = saveArea->method; 1010 1011 if (dvmIsBreakFrame(fp)) 1012 break; 1013 if (!dvmInstanceof(method->clazz, gDvm.classJavaLangThrowable)) 1014 break; 1015 //LOGD("EXCEP: ignoring %s.%s\n", 1016 // method->clazz->descriptor, method->name); 1017 fp = saveArea->prevFrame; 1018 } 1019 startFp = fp; 1020 1021 /* 1022 * Compute the stack depth. 1023 */ 1024 stackDepth = 0; 1025 while (fp != NULL) { 1026 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 1027 1028 if (!dvmIsBreakFrame(fp)) 1029 stackDepth++; 1030 1031 assert(fp != saveArea->prevFrame); 1032 fp = saveArea->prevFrame; 1033 } 1034 //LOGD("EXCEP: stack depth is %d\n", stackDepth); 1035 1036 if (!stackDepth) 1037 goto bail; 1038 1039 /* 1040 * We need to store a pointer to the Method and the program counter. 1041 * We have 4-byte pointers, so we use '[I'. 1042 */ 1043 if (wantObject) { 1044 assert(sizeof(Method*) == 4); 1045 stackData = dvmAllocPrimitiveArray('I', stackDepth*2, ALLOC_DEFAULT); 1046 if (stackData == NULL) { 1047 assert(dvmCheckException(dvmThreadSelf())); 1048 goto bail; 1049 } 1050 intPtr = (int*) stackData->contents; 1051 } else { 1052 /* array of ints; first entry is stack depth */ 1053 assert(sizeof(Method*) == sizeof(int)); 1054 simpleData = (int*) malloc(sizeof(int) * stackDepth*2); 1055 if (simpleData == NULL) 1056 goto bail; 1057 1058 assert(pCount != NULL); 1059 intPtr = simpleData; 1060 } 1061 if (pCount != NULL) 1062 *pCount = stackDepth; 1063 1064 fp = startFp; 1065 while (fp != NULL) { 1066 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 1067 const Method* method = saveArea->method; 1068 1069 if (!dvmIsBreakFrame(fp)) { 1070 //LOGD("EXCEP keeping %s.%s\n", method->clazz->descriptor, 1071 // method->name); 1072 1073 *intPtr++ = (int) method; 1074 if (dvmIsNativeMethod(method)) { 1075 *intPtr++ = 0; /* no saved PC for native methods */ 1076 } else { 1077 assert(saveArea->xtra.currentPc >= method->insns && 1078 saveArea->xtra.currentPc < 1079 method->insns + dvmGetMethodInsnsSize(method)); 1080 *intPtr++ = (int) (saveArea->xtra.currentPc - method->insns); 1081 } 1082 1083 stackDepth--; // for verification 1084 } 1085 1086 assert(fp != saveArea->prevFrame); 1087 fp = saveArea->prevFrame; 1088 } 1089 assert(stackDepth == 0); 1090 1091 bail: 1092 if (wantObject) { 1093 dvmReleaseTrackedAlloc((Object*) stackData, dvmThreadSelf()); 1094 return stackData; 1095 } else { 1096 return simpleData; 1097 } 1098 } 1099 1100 1101 /* 1102 * Given an Object previously created by dvmFillInStackTrace(), use the 1103 * contents of the saved stack trace to generate an array of 1104 * java/lang/StackTraceElement objects. 1105 * 1106 * The returned array is not added to the "local refs" list. 1107 */ 1108 ArrayObject* dvmGetStackTrace(const Object* ostackData) 1109 { 1110 const ArrayObject* stackData = (const ArrayObject*) ostackData; 1111 const int* intVals; 1112 int i, stackSize; 1113 1114 stackSize = stackData->length / 2; 1115 intVals = (const int*) stackData->contents; 1116 return dvmGetStackTraceRaw(intVals, stackSize); 1117 } 1118 1119 /* 1120 * Generate an array of StackTraceElement objects from the raw integer 1121 * data encoded by dvmFillInStackTrace(). 1122 * 1123 * "intVals" points to the first {method,pc} pair. 1124 * 1125 * The returned array is not added to the "local refs" list. 1126 */ 1127 ArrayObject* dvmGetStackTraceRaw(const int* intVals, int stackDepth) 1128 { 1129 ArrayObject* steArray = NULL; 1130 Object** stePtr; 1131 int i; 1132 1133 /* init this if we haven't yet */ 1134 if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement)) 1135 dvmInitClass(gDvm.classJavaLangStackTraceElement); 1136 1137 /* allocate a StackTraceElement array */ 1138 steArray = dvmAllocArray(gDvm.classJavaLangStackTraceElementArray, 1139 stackDepth, kObjectArrayRefWidth, ALLOC_DEFAULT); 1140 if (steArray == NULL) 1141 goto bail; 1142 stePtr = (Object**) steArray->contents; 1143 1144 /* 1145 * Allocate and initialize a StackTraceElement for each stack frame. 1146 * We use the standard constructor to configure the object. 1147 */ 1148 for (i = 0; i < stackDepth; i++) { 1149 Object* ste; 1150 Method* meth; 1151 StringObject* className; 1152 StringObject* methodName; 1153 StringObject* fileName; 1154 int lineNumber, pc; 1155 const char* sourceFile; 1156 char* dotName; 1157 1158 ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT); 1159 if (ste == NULL) 1160 goto bail; 1161 1162 meth = (Method*) *intVals++; 1163 pc = *intVals++; 1164 1165 if (pc == -1) // broken top frame? 1166 lineNumber = 0; 1167 else 1168 lineNumber = dvmLineNumFromPC(meth, pc); 1169 1170 dotName = dvmDescriptorToDot(meth->clazz->descriptor); 1171 className = dvmCreateStringFromCstr(dotName, ALLOC_DEFAULT); 1172 free(dotName); 1173 1174 methodName = dvmCreateStringFromCstr(meth->name, ALLOC_DEFAULT); 1175 sourceFile = dvmGetMethodSourceFile(meth); 1176 if (sourceFile != NULL) 1177 fileName = dvmCreateStringFromCstr(sourceFile, ALLOC_DEFAULT); 1178 else 1179 fileName = NULL; 1180 1181 /* 1182 * Invoke: 1183 * public StackTraceElement(String declaringClass, String methodName, 1184 * String fileName, int lineNumber) 1185 * (where lineNumber==-2 means "native") 1186 */ 1187 JValue unused; 1188 dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init, 1189 ste, &unused, className, methodName, fileName, lineNumber); 1190 1191 dvmReleaseTrackedAlloc(ste, NULL); 1192 dvmReleaseTrackedAlloc((Object*) className, NULL); 1193 dvmReleaseTrackedAlloc((Object*) methodName, NULL); 1194 dvmReleaseTrackedAlloc((Object*) fileName, NULL); 1195 1196 if (dvmCheckException(dvmThreadSelf())) 1197 goto bail; 1198 1199 *stePtr++ = ste; 1200 } 1201 1202 bail: 1203 dvmReleaseTrackedAlloc((Object*) steArray, NULL); 1204 return steArray; 1205 } 1206 1207 /* 1208 * Dump the contents of a raw stack trace to the log. 1209 */ 1210 void dvmLogRawStackTrace(const int* intVals, int stackDepth) 1211 { 1212 int i; 1213 1214 /* 1215 * Run through the array of stack frame data. 1216 */ 1217 for (i = 0; i < stackDepth; i++) { 1218 Method* meth; 1219 int lineNumber, pc; 1220 const char* sourceFile; 1221 char* dotName; 1222 1223 meth = (Method*) *intVals++; 1224 pc = *intVals++; 1225 1226 if (pc == -1) // broken top frame? 1227 lineNumber = 0; 1228 else 1229 lineNumber = dvmLineNumFromPC(meth, pc); 1230 1231 // probably don't need to do this, but it looks nicer 1232 dotName = dvmDescriptorToDot(meth->clazz->descriptor); 1233 1234 if (dvmIsNativeMethod(meth)) { 1235 LOGI("\tat %s.%s(Native Method)\n", dotName, meth->name); 1236 } else { 1237 LOGI("\tat %s.%s(%s:%d)\n", 1238 dotName, meth->name, dvmGetMethodSourceFile(meth), 1239 dvmLineNumFromPC(meth, pc)); 1240 } 1241 1242 free(dotName); 1243 1244 sourceFile = dvmGetMethodSourceFile(meth); 1245 } 1246 } 1247 1248 /* 1249 * Print the direct stack trace of the given exception to the log. 1250 */ 1251 static void logStackTraceOf(Object* exception) 1252 { 1253 const ArrayObject* stackData; 1254 StringObject* messageStr; 1255 int stackSize; 1256 const int* intVals; 1257 1258 messageStr = (StringObject*) dvmGetFieldObject(exception, 1259 gDvm.offJavaLangThrowable_message); 1260 if (messageStr != NULL) { 1261 char* cp = dvmCreateCstrFromString(messageStr); 1262 LOGI("%s: %s\n", exception->clazz->descriptor, cp); 1263 free(cp); 1264 } else { 1265 LOGI("%s:\n", exception->clazz->descriptor); 1266 } 1267 1268 stackData = (const ArrayObject*) dvmGetFieldObject(exception, 1269 gDvm.offJavaLangThrowable_stackState); 1270 if (stackData == NULL) { 1271 LOGI(" (no stack trace data found)\n"); 1272 return; 1273 } 1274 1275 stackSize = stackData->length / 2; 1276 intVals = (const int*) stackData->contents; 1277 1278 dvmLogRawStackTrace(intVals, stackSize); 1279 } 1280 1281 /* 1282 * Print the stack trace of the current thread's exception, as well as 1283 * the stack traces of any chained exceptions, to the log. We extract 1284 * the stored stack trace and process it internally instead of calling 1285 * interpreted code. 1286 */ 1287 void dvmLogExceptionStackTrace(void) 1288 { 1289 Object* exception = dvmThreadSelf()->exception; 1290 Object* cause; 1291 1292 if (exception == NULL) { 1293 LOGW("tried to log a null exception?\n"); 1294 return; 1295 } 1296 1297 for (;;) { 1298 logStackTraceOf(exception); 1299 cause = dvmGetExceptionCause(exception); 1300 if (cause == NULL) { 1301 break; 1302 } 1303 LOGI("Caused by:\n"); 1304 exception = cause; 1305 } 1306 } 1307 1308