Exception.cpp revision 9b598e34e2401bda77fca9c8f3a5c50e882e6d68
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/* 25Notes on Exception Handling 26 27We have one fairly sticky issue to deal with: creating the exception stack 28trace. The trouble is that we need the current value of the program 29counter for the method now being executed, but that's only held in a local 30variable or hardware register in the main interpreter loop. 31 32The exception mechanism requires that the current stack trace be associated 33with a Throwable at the time the Throwable is constructed. The construction 34may or may not be associated with a throw. We have three situations to 35consider: 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 44We need to have the current value for the PC, which means that for 45situation (3) the interpreter loop must copy it to an externally-accessible 46location before handling any opcode that could cause the VM to throw 47an exception. We can't store it globally, because the various threads 48would trample each other. We can't store it in the Thread structure, 49because it'll get overwritten as soon as the Throwable constructor starts 50executing. It needs to go on the stack, but our stack frames hold the 51caller's *saved* PC, not the current PC. 52 53Situation #1 doesn't require special handling. Situation #2 could be dealt 54with by passing the PC into the exception creation function. The trick 55is to solve situation #3 in a way that adds minimal overhead to common 56operations. Making it more costly to throw an exception is acceptable. 57 58There 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 72Solution (a) is the simplest, but breaks Dalvik's goal of mingling native 73and interpreted stacks. 74 75Solution (b) retains the simplicity of (a) without rearranging the stack, 76but now in some cases we're storing the PC twice, which feels wrong. 77 78Solution (c) usually works, because we push the saved PC onto the stack 79before the Throwable construction can overwrite the copy in Thread. One 80way 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) 84This is a risk for anything that could cause <clinit> to execute, e.g. 85executing a static method or accessing a static field. Attemping to access 86a field that doesn't exist in a class that does exist might cause this. 87It may be possible to simply bracket the dvmCallMethod*() functions to 88save/restore it. 89 90Solution (d) incurs additional overhead, but may have other benefits (e.g. 91it's easy to find the stack frames that should be removed before storage 92in the Throwable). 93 94Current plan is option (b), because it's simple, fast, and doesn't change 95the way the stack works. 96*/ 97 98/* fwd */ 99static bool initException(Object* exception, const char* msg, Object* cause, 100 Thread* self); 101 102 103/* 104 * Helper for dvmExceptionStartup(), which looks up classes and stores 105 * them to the indicated pointer, returning a failure code (false == 106 * failure). 107 */ 108static bool initRef(ClassObject** pClass, const char* name) 109{ 110 ClassObject* result; 111 112 if (name[0] == '[') { 113 result = dvmFindArrayClass(name, NULL); 114 } else { 115 result = dvmFindSystemClassNoInit(name); 116 } 117 118 if (result == NULL) { 119 LOGE("Could not find exception class %s\n", name); 120 return false; 121 } 122 123 *pClass = result; 124 return true; 125} 126 127/* 128 * Cache pointers to some of the exception classes we use locally. 129 * 130 * Note this is NOT called during dexopt optimization. Some of the fields 131 * are initialized by the verifier (dvmVerifyCodeFlow). 132 */ 133bool dvmExceptionStartup(void) 134{ 135 bool ok = true; 136 137 ok &= initRef(&gDvm.exArithmeticException, 138 "Ljava/lang/ArithmeticException;"); 139 ok &= initRef(&gDvm.exArrayIndexOutOfBoundsException, 140 "Ljava/lang/ArrayIndexOutOfBoundsException;"); 141 ok &= initRef(&gDvm.exArrayStoreException, 142 "Ljava/lang/ArrayStoreException;"); 143 ok &= initRef(&gDvm.exClassCastException, 144 "Ljava/lang/ClassCastException;"); 145 ok &= initRef(&gDvm.exClassNotFoundException, 146 "Ljava/lang/ClassNotFoundException;"); 147 ok &= initRef(&gDvm.exClassFormatError, "Ljava/lang/ClassFormatError;"); 148 ok &= initRef(&gDvm.exError, "Ljava/lang/Error;"); 149 ok &= initRef(&gDvm.exExceptionInInitializerError, 150 "Ljava/lang/ExceptionInInitializerError;"); 151 ok &= initRef(&gDvm.exFileNotFoundException, 152 "Ljava/io/FileNotFoundException;"); 153 ok &= initRef(&gDvm.exIOException, "Ljava/io/IOException;"); 154 ok &= initRef(&gDvm.exIllegalAccessException, 155 "Ljava/lang/IllegalAccessException;"); 156 ok &= initRef(&gDvm.exInterruptedException, 157 "Ljava/lang/InterruptedException;"); 158 ok &= initRef(&gDvm.exNegativeArraySizeException, 159 "Ljava/lang/NegativeArraySizeException;"); 160 ok &= initRef(&gDvm.exNoSuchFieldException, 161 "Ljava/lang/NoSuchFieldException;"); 162 ok &= initRef(&gDvm.exNullPointerException, 163 "Ljava/lang/NullPointerException;"); 164 ok &= initRef(&gDvm.exRuntimeException, "Ljava/lang/RuntimeException;"); 165 ok &= initRef(&gDvm.exStackOverflowError, 166 "Ljava/lang/StackOverflowError;"); 167 ok &= initRef(&gDvm.exStaleDexCacheError, 168 "Ldalvik/system/StaleDexCacheError;"); 169 ok &= initRef(&gDvm.exStringIndexOutOfBoundsException, 170 "Ljava/lang/StringIndexOutOfBoundsException;"); 171 ok &= initRef(&gDvm.exThrowable, "Ljava/lang/Throwable;"); 172 ok &= initRef(&gDvm.exUnsupportedOperationException, 173 "Ljava/lang/UnsupportedOperationException;"); 174 ok &= initRef(&gDvm.exVirtualMachineError, 175 "Ljava/lang/VirtualMachineError;"); 176 177 ok &= initRef(&gDvm.classJavaLangStackTraceElement, 178 "Ljava/lang/StackTraceElement;"); 179 ok &= initRef(&gDvm.classJavaLangStackTraceElementArray, 180 "[Ljava/lang/StackTraceElement;"); 181 182 if (!ok) { 183 return false; 184 } 185 186 /* 187 * Find the constructor. Note that, unlike other saved method lookups, 188 * we're using a Method* instead of a vtable offset. This is because 189 * constructors don't have vtable offsets. (Also, since we're creating 190 * the object in question, it's impossible for anyone to sub-class it.) 191 */ 192 Method* meth; 193 meth = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangStackTraceElement, 194 "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V"); 195 if (meth == NULL) { 196 LOGE("Unable to find constructor for StackTraceElement\n"); 197 return false; 198 } 199 gDvm.methJavaLangStackTraceElement_init = meth; 200 201 /* grab an offset for the stackData field */ 202 gDvm.offJavaLangThrowable_stackState = 203 dvmFindFieldOffset(gDvm.exThrowable, 204 "stackState", "Ljava/lang/Object;"); 205 if (gDvm.offJavaLangThrowable_stackState < 0) { 206 LOGE("Unable to find Throwable.stackState\n"); 207 return false; 208 } 209 210 /* and one for the cause field, just 'cause */ 211 gDvm.offJavaLangThrowable_cause = 212 dvmFindFieldOffset(gDvm.exThrowable, 213 "cause", "Ljava/lang/Throwable;"); 214 if (gDvm.offJavaLangThrowable_cause < 0) { 215 LOGE("Unable to find Throwable.cause\n"); 216 return false; 217 } 218 219 /* 220 * ExceptionInInitializerError is used in the guts of Class.c; it 221 * wants to call the constructor more directly, so look that up 222 * explicitly, here. 223 */ 224 gDvm.methJavaLangExceptionInInitializerError_init = 225 dvmFindDirectMethodByDescriptor(gDvm.exExceptionInInitializerError, 226 "<init>", "(Ljava/lang/Throwable;)V"); 227 if (gDvm.methJavaLangExceptionInInitializerError_init == NULL) { 228 LOGE("Unable to prep java/lang/ExceptionInInitializerError\n"); 229 return false; 230 } 231 232 return true; 233} 234 235/* 236 * Clean up. 237 */ 238void dvmExceptionShutdown(void) 239{ 240 // nothing to do 241} 242 243 244/* 245 * Format the message into a small buffer and pass it along. 246 */ 247void dvmThrowExceptionFmtV(const char* exceptionDescriptor, const char* fmt, 248 va_list args) 249{ 250 char msgBuf[512]; 251 252 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); 253 dvmThrowChainedException(exceptionDescriptor, msgBuf, NULL); 254} 255 256void dvmThrowExceptionFmtByClassV(ClassObject* exceptionClass, 257 const char* fmt, va_list args) 258{ 259 char msgBuf[512]; 260 261 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); 262 dvmThrowChainedExceptionByClass(exceptionClass, msgBuf, NULL); 263} 264 265/* 266 * Create a Throwable and throw an exception in the current thread (where 267 * "throwing" just means "set the thread's exception pointer"). 268 * 269 * "msg" and/or "cause" may be NULL. 270 * 271 * If we have a bad exception hierarchy -- something in Throwable.<init> 272 * is missing -- then every attempt to throw an exception will result 273 * in another exception. Exceptions are generally allowed to "chain" 274 * to other exceptions, so it's hard to auto-detect this problem. It can 275 * only happen if the system classes are broken, so it's probably not 276 * worth spending cycles to detect it. 277 * 278 * We do have one case to worry about: if the classpath is completely 279 * wrong, we'll go into a death spin during startup because we can't find 280 * the initial class and then we can't find NoClassDefFoundError. We have 281 * to handle this case. 282 * 283 * [Do we want to cache pointers to common exception classes?] 284 */ 285void dvmThrowChainedException(const char* exceptionDescriptor, const char* msg, 286 Object* cause) 287{ 288 ClassObject* excepClass; 289 290 LOGV("THROW '%s' msg='%s' cause=%s\n", 291 exceptionDescriptor, msg, 292 (cause != NULL) ? cause->clazz->descriptor : "(none)"); 293 294 if (gDvm.initializing) { 295 if (++gDvm.initExceptionCount >= 2) { 296 LOGE("Too many exceptions during init (failed on '%s' '%s')\n", 297 exceptionDescriptor, msg); 298 dvmAbort(); 299 } 300 } 301 302 excepClass = dvmFindSystemClass(exceptionDescriptor); 303 if (excepClass == NULL) { 304 /* 305 * We couldn't find the exception class. The attempt to find a 306 * nonexistent class should have raised an exception. If no 307 * exception is currently raised, then we're pretty clearly unable 308 * to throw ANY sort of exception, and we need to pack it in. 309 * 310 * If we were able to throw the "class load failed" exception, 311 * stick with that. Ideally we'd stuff the original exception 312 * into the "cause" field, but since we can't find it we can't 313 * do that. The exception class name should be in the "message" 314 * field. 315 */ 316 if (!dvmCheckException(dvmThreadSelf())) { 317 LOGE("FATAL: unable to throw exception (failed on '%s' '%s')\n", 318 exceptionDescriptor, msg); 319 dvmAbort(); 320 } 321 return; 322 } 323 324 dvmThrowChainedExceptionByClass(excepClass, msg, cause); 325} 326 327/* 328 * Start/continue throwing process now that we have a class reference. 329 */ 330void dvmThrowChainedExceptionByClass(ClassObject* excepClass, const char* msg, 331 Object* cause) 332{ 333 Thread* self = dvmThreadSelf(); 334 Object* exception; 335 336 if (excepClass == NULL) { 337 /* 338 * The exception class was passed in as NULL. This might happen 339 * early on in VM initialization. There's nothing better to do 340 * than just log the message as an error and abort. 341 */ 342 LOGE("Fatal error: %s\n", msg); 343 dvmAbort(); 344 } 345 346 /* make sure the exception is initialized */ 347 if (!dvmIsClassInitialized(excepClass) && !dvmInitClass(excepClass)) { 348 LOGE("ERROR: unable to initialize exception class '%s'\n", 349 excepClass->descriptor); 350 if (strcmp(excepClass->descriptor, "Ljava/lang/InternalError;") == 0) 351 dvmAbort(); 352 dvmThrowChainedException("Ljava/lang/InternalError;", 353 "failed to init original exception class", cause); 354 return; 355 } 356 357 exception = dvmAllocObject(excepClass, ALLOC_DEFAULT); 358 if (exception == NULL) { 359 /* 360 * We're in a lot of trouble. We might be in the process of 361 * throwing an out-of-memory exception, in which case the 362 * pre-allocated object will have been thrown when our object alloc 363 * failed. So long as there's an exception raised, return and 364 * allow the system to try to recover. If not, something is broken 365 * and we need to bail out. 366 */ 367 if (dvmCheckException(self)) 368 goto bail; 369 LOGE("FATAL: unable to allocate exception '%s' '%s'\n", 370 excepClass->descriptor, msg != NULL ? msg : "(no msg)"); 371 dvmAbort(); 372 } 373 374 /* 375 * Init the exception. 376 */ 377 if (gDvm.optimizing) { 378 /* need the exception object, but can't invoke interpreted code */ 379 LOGV("Skipping init of exception %s '%s'\n", 380 excepClass->descriptor, msg); 381 } else { 382 assert(excepClass == exception->clazz); 383 if (!initException(exception, msg, cause, self)) { 384 /* 385 * Whoops. If we can't initialize the exception, we can't use 386 * it. If there's an exception already set, the constructor 387 * probably threw an OutOfMemoryError. 388 */ 389 if (!dvmCheckException(self)) { 390 /* 391 * We're required to throw something, so we just 392 * throw the pre-constructed internal error. 393 */ 394 self->exception = gDvm.internalErrorObj; 395 } 396 goto bail; 397 } 398 } 399 400 self->exception = exception; 401 402bail: 403 dvmReleaseTrackedAlloc(exception, self); 404} 405 406/* 407 * Throw the named exception using the human-readable form of the class 408 * descriptor as the exception message, and with the specified cause. 409 */ 410void dvmThrowChainedExceptionWithClassMessage(const char* exceptionDescriptor, 411 const char* messageDescriptor, Object* cause) 412{ 413 char* message = dvmHumanReadableDescriptor(messageDescriptor); 414 415 dvmThrowChainedException(exceptionDescriptor, message, cause); 416 free(message); 417} 418 419/* 420 * Like dvmThrowException, but take a class object instead of a name 421 * and turn the given message into the human-readable form for a descriptor. 422 */ 423void dvmThrowExceptionByClassWithClassMessage(ClassObject* exceptionClass, 424 const char* messageDescriptor) 425{ 426 char* message = dvmDescriptorToName(messageDescriptor); 427 428 dvmThrowExceptionByClass(exceptionClass, message); 429 free(message); 430} 431 432/* 433 * Find and return an exception constructor method that can take the 434 * indicated parameters, or return NULL if no such constructor exists. 435 */ 436static Method* findExceptionInitMethod(ClassObject* excepClass, 437 bool hasMessage, bool hasCause) 438{ 439 if (hasMessage) { 440 Method* result; 441 442 if (hasCause) { 443 result = dvmFindDirectMethodByDescriptor( 444 excepClass, "<init>", 445 "(Ljava/lang/String;Ljava/lang/Throwable;)V"); 446 } else { 447 result = dvmFindDirectMethodByDescriptor( 448 excepClass, "<init>", "(Ljava/lang/String;)V"); 449 } 450 451 if (result != NULL) { 452 return result; 453 } 454 455 if (hasCause) { 456 return dvmFindDirectMethodByDescriptor( 457 excepClass, "<init>", 458 "(Ljava/lang/Object;Ljava/lang/Throwable;)V"); 459 } else { 460 return dvmFindDirectMethodByDescriptor( 461 excepClass, "<init>", "(Ljava/lang/Object;)V"); 462 } 463 } else if (hasCause) { 464 return dvmFindDirectMethodByDescriptor( 465 excepClass, "<init>", "(Ljava/lang/Throwable;)V"); 466 } else { 467 return dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V"); 468 } 469} 470 471/* 472 * Initialize an exception with an appropriate constructor. 473 * 474 * "exception" is the exception object to initialize. 475 * Either or both of "msg" and "cause" may be null. 476 * "self" is dvmThreadSelf(), passed in so we don't have to look it up again. 477 * 478 * If the process of initializing the exception causes another 479 * exception (e.g., OutOfMemoryError) to be thrown, return an error 480 * and leave self->exception intact. 481 */ 482static bool initException(Object* exception, const char* msg, Object* cause, 483 Thread* self) 484{ 485 enum { 486 kInitUnknown, 487 kInitNoarg, 488 kInitMsg, 489 kInitMsgThrow, 490 kInitThrow 491 } initKind = kInitUnknown; 492 Method* initMethod = NULL; 493 ClassObject* excepClass = exception->clazz; 494 StringObject* msgStr = NULL; 495 bool result = false; 496 bool needInitCause = false; 497 498 assert(self != NULL); 499 assert(self->exception == NULL); 500 501 /* if we have a message, create a String */ 502 if (msg == NULL) 503 msgStr = NULL; 504 else { 505 msgStr = dvmCreateStringFromCstr(msg); 506 if (msgStr == NULL) { 507 LOGW("Could not allocate message string \"%s\" while " 508 "throwing internal exception (%s)\n", 509 msg, excepClass->descriptor); 510 goto bail; 511 } 512 } 513 514 if (cause != NULL) { 515 if (!dvmInstanceof(cause->clazz, gDvm.exThrowable)) { 516 LOGE("Tried to init exception with cause '%s'\n", 517 cause->clazz->descriptor); 518 dvmAbort(); 519 } 520 } 521 522 /* 523 * The Throwable class has four public constructors: 524 * (1) Throwable() 525 * (2) Throwable(String message) 526 * (3) Throwable(String message, Throwable cause) (added in 1.4) 527 * (4) Throwable(Throwable cause) (added in 1.4) 528 * 529 * The first two are part of the original design, and most exception 530 * classes should support them. The third prototype was used by 531 * individual exceptions. e.g. ClassNotFoundException added it in 1.2. 532 * The general "cause" mechanism was added in 1.4. Some classes, 533 * such as IllegalArgumentException, initially supported the first 534 * two, but added the second two in a later release. 535 * 536 * Exceptions may be picky about how their "cause" field is initialized. 537 * If you call ClassNotFoundException(String), it may choose to 538 * initialize its "cause" field to null. Doing so prevents future 539 * calls to Throwable.initCause(). 540 * 541 * So, if "cause" is not NULL, we need to look for a constructor that 542 * takes a throwable. If we can't find one, we fall back on calling 543 * #1/#2 and making a separate call to initCause(). Passing a null ref 544 * for "message" into Throwable(String, Throwable) is allowed, but we 545 * prefer to use the Throwable-only version because it has different 546 * behavior. 547 * 548 * java.lang.TypeNotPresentException is a strange case -- it has #3 but 549 * not #2. (Some might argue that the constructor is actually not #3, 550 * because it doesn't take the message string as an argument, but it 551 * has the same effect and we can work with it here.) 552 * 553 * java.lang.AssertionError is also a strange case -- it has a 554 * constructor that takes an Object, but not one that takes a String. 555 * There may be other cases like this, as well, so we generally look 556 * for an Object-taking constructor if we can't find one that takes 557 * a String. 558 */ 559 if (cause == NULL) { 560 if (msgStr == NULL) { 561 initMethod = findExceptionInitMethod(excepClass, false, false); 562 initKind = kInitNoarg; 563 } else { 564 initMethod = findExceptionInitMethod(excepClass, true, false); 565 if (initMethod != NULL) { 566 initKind = kInitMsg; 567 } else { 568 /* no #2, try #3 */ 569 initMethod = findExceptionInitMethod(excepClass, true, true); 570 if (initMethod != NULL) { 571 initKind = kInitMsgThrow; 572 } 573 } 574 } 575 } else { 576 if (msgStr == NULL) { 577 initMethod = findExceptionInitMethod(excepClass, false, true); 578 if (initMethod != NULL) { 579 initKind = kInitThrow; 580 } else { 581 initMethod = findExceptionInitMethod(excepClass, false, false); 582 initKind = kInitNoarg; 583 needInitCause = true; 584 } 585 } else { 586 initMethod = findExceptionInitMethod(excepClass, true, true); 587 if (initMethod != NULL) { 588 initKind = kInitMsgThrow; 589 } else { 590 initMethod = findExceptionInitMethod(excepClass, true, false); 591 initKind = kInitMsg; 592 needInitCause = true; 593 } 594 } 595 } 596 597 if (initMethod == NULL) { 598 /* 599 * We can't find the desired constructor. This can happen if a 600 * subclass of java/lang/Throwable doesn't define an expected 601 * constructor, e.g. it doesn't provide one that takes a string 602 * when a message has been provided. 603 */ 604 LOGW("WARNING: exception class '%s' missing constructor " 605 "(msg='%s' kind=%d)\n", 606 excepClass->descriptor, msg, initKind); 607 assert(strcmp(excepClass->descriptor, 608 "Ljava/lang/RuntimeException;") != 0); 609 dvmThrowChainedExceptionByClass(gDvm.exRuntimeException, 610 "re-throw on exception class missing constructor", NULL); 611 goto bail; 612 } 613 614 /* 615 * Call the constructor with the appropriate arguments. 616 */ 617 JValue unused; 618 switch (initKind) { 619 case kInitNoarg: 620 LOGVV("+++ exc noarg (ic=%d)\n", needInitCause); 621 dvmCallMethod(self, initMethod, exception, &unused); 622 break; 623 case kInitMsg: 624 LOGVV("+++ exc msg (ic=%d)\n", needInitCause); 625 dvmCallMethod(self, initMethod, exception, &unused, msgStr); 626 break; 627 case kInitThrow: 628 LOGVV("+++ exc throw"); 629 assert(!needInitCause); 630 dvmCallMethod(self, initMethod, exception, &unused, cause); 631 break; 632 case kInitMsgThrow: 633 LOGVV("+++ exc msg+throw"); 634 assert(!needInitCause); 635 dvmCallMethod(self, initMethod, exception, &unused, msgStr, cause); 636 break; 637 default: 638 assert(false); 639 goto bail; 640 } 641 642 /* 643 * It's possible the constructor has thrown an exception. If so, we 644 * return an error and let our caller deal with it. 645 */ 646 if (self->exception != NULL) { 647 LOGW("Exception thrown (%s) while throwing internal exception (%s)\n", 648 self->exception->clazz->descriptor, exception->clazz->descriptor); 649 goto bail; 650 } 651 652 /* 653 * If this exception was caused by another exception, and we weren't 654 * able to find a cause-setting constructor, set the "cause" field 655 * with an explicit call. 656 */ 657 if (needInitCause) { 658 Method* initCause; 659 initCause = dvmFindVirtualMethodHierByDescriptor(excepClass, "initCause", 660 "(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); 661 if (initCause != NULL) { 662 dvmCallMethod(self, initCause, exception, &unused, cause); 663 if (self->exception != NULL) { 664 /* initCause() threw an exception; return an error and 665 * let the caller deal with it. 666 */ 667 LOGW("Exception thrown (%s) during initCause() " 668 "of internal exception (%s)\n", 669 self->exception->clazz->descriptor, 670 exception->clazz->descriptor); 671 goto bail; 672 } 673 } else { 674 LOGW("WARNING: couldn't find initCause in '%s'\n", 675 excepClass->descriptor); 676 } 677 } 678 679 680 result = true; 681 682bail: 683 dvmReleaseTrackedAlloc((Object*) msgStr, self); // NULL is ok 684 return result; 685} 686 687 688/* 689 * Clear the pending exception and the "initExceptionCount" counter. This 690 * is used by the optimization and verification code, which has to run with 691 * "initializing" set to avoid going into a death-spin if the "class not 692 * found" exception can't be found. 693 * 694 * This can also be called when the VM is in a "normal" state, e.g. when 695 * verifying classes that couldn't be verified at optimization time. The 696 * reset of initExceptionCount should be harmless in that case. 697 */ 698void dvmClearOptException(Thread* self) 699{ 700 self->exception = NULL; 701 gDvm.initExceptionCount = 0; 702} 703 704/* 705 * Returns "true" if this is a "checked" exception, i.e. it's a subclass 706 * of Throwable (assumed) but not a subclass of RuntimeException or Error. 707 */ 708bool dvmIsCheckedException(const Object* exception) 709{ 710 if (dvmInstanceof(exception->clazz, gDvm.exError) || 711 dvmInstanceof(exception->clazz, gDvm.exRuntimeException)) 712 { 713 return false; 714 } else { 715 return true; 716 } 717} 718 719/* 720 * Wrap the now-pending exception in a different exception. This is useful 721 * for reflection stuff that wants to hand a checked exception back from a 722 * method that doesn't declare it. 723 * 724 * If something fails, an (unchecked) exception related to that failure 725 * will be pending instead. 726 */ 727void dvmWrapException(const char* newExcepStr) 728{ 729 Thread* self = dvmThreadSelf(); 730 Object* origExcep; 731 ClassObject* iteClass; 732 733 origExcep = dvmGetException(self); 734 dvmAddTrackedAlloc(origExcep, self); // don't let the GC free it 735 736 dvmClearException(self); // clear before class lookup 737 iteClass = dvmFindSystemClass(newExcepStr); 738 if (iteClass != NULL) { 739 Object* iteExcep; 740 Method* initMethod; 741 742 iteExcep = dvmAllocObject(iteClass, ALLOC_DEFAULT); 743 if (iteExcep != NULL) { 744 initMethod = dvmFindDirectMethodByDescriptor(iteClass, "<init>", 745 "(Ljava/lang/Throwable;)V"); 746 if (initMethod != NULL) { 747 JValue unused; 748 dvmCallMethod(self, initMethod, iteExcep, &unused, 749 origExcep); 750 751 /* if <init> succeeded, replace the old exception */ 752 if (!dvmCheckException(self)) 753 dvmSetException(self, iteExcep); 754 } 755 dvmReleaseTrackedAlloc(iteExcep, NULL); 756 757 /* if initMethod doesn't exist, or failed... */ 758 if (!dvmCheckException(self)) 759 dvmSetException(self, origExcep); 760 } else { 761 /* leave OutOfMemoryError pending */ 762 } 763 } else { 764 /* leave ClassNotFoundException pending */ 765 } 766 767 assert(dvmCheckException(self)); 768 dvmReleaseTrackedAlloc(origExcep, self); 769} 770 771/* 772 * Get the "cause" field from an exception. 773 * 774 * The Throwable class initializes the "cause" field to "this" to 775 * differentiate between being initialized to null and never being 776 * initialized. We check for that here and convert it to NULL. 777 */ 778Object* dvmGetExceptionCause(const Object* exception) 779{ 780 if (!dvmInstanceof(exception->clazz, gDvm.exThrowable)) { 781 LOGE("Tried to get cause from object of type '%s'\n", 782 exception->clazz->descriptor); 783 dvmAbort(); 784 } 785 Object* cause = 786 dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause); 787 if (cause == exception) 788 return NULL; 789 else 790 return cause; 791} 792 793/* 794 * Print the stack trace of the current exception on stderr. This is called 795 * from the JNI ExceptionDescribe call. 796 * 797 * For consistency we just invoke the Throwable printStackTrace method, 798 * which might be overridden in the exception object. 799 * 800 * Exceptions thrown during the course of printing the stack trace are 801 * ignored. 802 */ 803void dvmPrintExceptionStackTrace(void) 804{ 805 Thread* self = dvmThreadSelf(); 806 Object* exception; 807 Method* printMethod; 808 809 exception = self->exception; 810 if (exception == NULL) 811 return; 812 813 dvmAddTrackedAlloc(exception, self); 814 self->exception = NULL; 815 printMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz, 816 "printStackTrace", "()V"); 817 if (printMethod != NULL) { 818 JValue unused; 819 dvmCallMethod(self, printMethod, exception, &unused); 820 } else { 821 LOGW("WARNING: could not find printStackTrace in %s\n", 822 exception->clazz->descriptor); 823 } 824 825 if (self->exception != NULL) { 826 LOGW("NOTE: exception thrown while printing stack trace: %s\n", 827 self->exception->clazz->descriptor); 828 } 829 830 self->exception = exception; 831 dvmReleaseTrackedAlloc(exception, self); 832} 833 834/* 835 * Search the method's list of exceptions for a match. 836 * 837 * Returns the offset of the catch block on success, or -1 on failure. 838 */ 839static int findCatchInMethod(Thread* self, const Method* method, int relPc, 840 ClassObject* excepClass) 841{ 842 /* 843 * Need to clear the exception before entry. Otherwise, dvmResolveClass 844 * might think somebody threw an exception while it was loading a class. 845 */ 846 assert(!dvmCheckException(self)); 847 assert(!dvmIsNativeMethod(method)); 848 849 LOGVV("findCatchInMethod %s.%s excep=%s depth=%d\n", 850 method->clazz->descriptor, method->name, excepClass->descriptor, 851 dvmComputeExactFrameDepth(self->curFrame)); 852 853 DvmDex* pDvmDex = method->clazz->pDvmDex; 854 const DexCode* pCode = dvmGetMethodCode(method); 855 DexCatchIterator iterator; 856 857 if (dexFindCatchHandler(&iterator, pCode, relPc)) { 858 for (;;) { 859 DexCatchHandler* handler = dexCatchIteratorNext(&iterator); 860 861 if (handler == NULL) { 862 break; 863 } 864 865 if (handler->typeIdx == kDexNoIndex) { 866 /* catch-all */ 867 LOGV("Match on catch-all block at 0x%02x in %s.%s for %s\n", 868 relPc, method->clazz->descriptor, 869 method->name, excepClass->descriptor); 870 return handler->address; 871 } 872 873 ClassObject* throwable = 874 dvmDexGetResolvedClass(pDvmDex, handler->typeIdx); 875 if (throwable == NULL) { 876 /* 877 * TODO: this behaves badly if we run off the stack 878 * while trying to throw an exception. The problem is 879 * that, if we're in a class loaded by a class loader, 880 * the call to dvmResolveClass has to ask the class 881 * loader for help resolving any previously-unresolved 882 * classes. If this particular class loader hasn't 883 * resolved StackOverflowError, it will call into 884 * interpreted code, and blow up. 885 * 886 * We currently replace the previous exception with 887 * the StackOverflowError, which means they won't be 888 * catching it *unless* they explicitly catch 889 * StackOverflowError, in which case we'll be unable 890 * to resolve the class referred to by the "catch" 891 * block. 892 * 893 * We end up getting a huge pile of warnings if we do 894 * a simple synthetic test, because this method gets 895 * called on every stack frame up the tree, and it 896 * fails every time. 897 * 898 * This eventually bails out, effectively becoming an 899 * uncatchable exception, so other than the flurry of 900 * warnings it's not really a problem. Still, we could 901 * probably handle this better. 902 */ 903 throwable = dvmResolveClass(method->clazz, handler->typeIdx, 904 true); 905 if (throwable == NULL) { 906 /* 907 * We couldn't find the exception they wanted in 908 * our class files (or, perhaps, the stack blew up 909 * while we were querying a class loader). Cough 910 * up a warning, then move on to the next entry. 911 * Keep the exception status clear. 912 */ 913 LOGW("Could not resolve class ref'ed in exception " 914 "catch list (class index %d, exception %s)\n", 915 handler->typeIdx, 916 (self->exception != NULL) ? 917 self->exception->clazz->descriptor : "(none)"); 918 dvmClearException(self); 919 continue; 920 } 921 } 922 923 //LOGD("ADDR MATCH, check %s instanceof %s\n", 924 // excepClass->descriptor, pEntry->excepClass->descriptor); 925 926 if (dvmInstanceof(excepClass, throwable)) { 927 LOGV("Match on catch block at 0x%02x in %s.%s for %s\n", 928 relPc, method->clazz->descriptor, 929 method->name, excepClass->descriptor); 930 return handler->address; 931 } 932 } 933 } 934 935 LOGV("No matching catch block at 0x%02x in %s for %s\n", 936 relPc, method->name, excepClass->descriptor); 937 return -1; 938} 939 940/* 941 * Find a matching "catch" block. "pc" is the relative PC within the 942 * current method, indicating the offset from the start in 16-bit units. 943 * 944 * Returns the offset to the catch block, or -1 if we run up against a 945 * break frame without finding anything. 946 * 947 * The class resolution stuff we have to do while evaluating the "catch" 948 * blocks could cause an exception. The caller should clear the exception 949 * before calling here and restore it after. 950 * 951 * Sets *newFrame to the frame pointer of the frame with the catch block. 952 * If "scanOnly" is false, self->curFrame is also set to this value. 953 */ 954int dvmFindCatchBlock(Thread* self, int relPc, Object* exception, 955 bool scanOnly, void** newFrame) 956{ 957 void* fp = self->curFrame; 958 int catchAddr = -1; 959 960 assert(!dvmCheckException(self)); 961 962 while (true) { 963 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 964 catchAddr = findCatchInMethod(self, saveArea->method, relPc, 965 exception->clazz); 966 if (catchAddr >= 0) 967 break; 968 969 /* 970 * Normally we'd check for ACC_SYNCHRONIZED methods and unlock 971 * them as we unroll. Dalvik uses what amount to generated 972 * "finally" blocks to take care of this for us. 973 */ 974 975 /* output method profiling info */ 976 if (!scanOnly) { 977 TRACE_METHOD_UNROLL(self, saveArea->method); 978 } 979 980 /* 981 * Move up one frame. If the next thing up is a break frame, 982 * break out now so we're left unrolled to the last method frame. 983 * We need to point there so we can roll up the JNI local refs 984 * if this was a native method. 985 */ 986 assert(saveArea->prevFrame != NULL); 987 if (dvmIsBreakFrame((u4*)saveArea->prevFrame)) { 988 if (!scanOnly) 989 break; // bail with catchAddr == -1 990 991 /* 992 * We're scanning for the debugger. It needs to know if this 993 * exception is going to be caught or not, and we need to figure 994 * out if it will be caught *ever* not just between the current 995 * position and the next break frame. We can't tell what native 996 * code is going to do, so we assume it never catches exceptions. 997 * 998 * Start by finding an interpreted code frame. 999 */ 1000 fp = saveArea->prevFrame; // this is the break frame 1001 saveArea = SAVEAREA_FROM_FP(fp); 1002 fp = saveArea->prevFrame; // this may be a good one 1003 while (fp != NULL) { 1004 if (!dvmIsBreakFrame((u4*)fp)) { 1005 saveArea = SAVEAREA_FROM_FP(fp); 1006 if (!dvmIsNativeMethod(saveArea->method)) 1007 break; 1008 } 1009 1010 fp = SAVEAREA_FROM_FP(fp)->prevFrame; 1011 } 1012 if (fp == NULL) 1013 break; // bail with catchAddr == -1 1014 1015 /* 1016 * Now fp points to the "good" frame. When the interp code 1017 * invoked the native code, it saved a copy of its current PC 1018 * into xtra.currentPc. Pull it out of there. 1019 */ 1020 relPc = 1021 saveArea->xtra.currentPc - SAVEAREA_FROM_FP(fp)->method->insns; 1022 } else { 1023 fp = saveArea->prevFrame; 1024 1025 /* savedPc in was-current frame goes with method in now-current */ 1026 relPc = saveArea->savedPc - SAVEAREA_FROM_FP(fp)->method->insns; 1027 } 1028 } 1029 1030 if (!scanOnly) 1031 self->curFrame = fp; 1032 1033 /* 1034 * The class resolution in findCatchInMethod() could cause an exception. 1035 * Clear it to be safe. 1036 */ 1037 self->exception = NULL; 1038 1039 *newFrame = fp; 1040 return catchAddr; 1041} 1042 1043/* 1044 * We have to carry the exception's stack trace around, but in many cases 1045 * it will never be examined. It makes sense to keep it in a compact, 1046 * VM-specific object, rather than an array of Objects with strings. 1047 * 1048 * Pass in the thread whose stack we're interested in. If "thread" is 1049 * not self, the thread must be suspended. This implies that the thread 1050 * list lock is held, which means we can't allocate objects or we risk 1051 * jamming the GC. So, we allow this function to return different formats. 1052 * (This shouldn't be called directly -- see the inline functions in the 1053 * header file.) 1054 * 1055 * If "wantObject" is true, this returns a newly-allocated Object, which is 1056 * presently an array of integers, but could become something else in the 1057 * future. If "wantObject" is false, return plain malloc data. 1058 * 1059 * NOTE: if we support class unloading, we will need to scan the class 1060 * object references out of these arrays. 1061 */ 1062void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, int* pCount) 1063{ 1064 ArrayObject* stackData = NULL; 1065 int* simpleData = NULL; 1066 void* fp; 1067 void* startFp; 1068 int stackDepth; 1069 int* intPtr; 1070 1071 if (pCount != NULL) 1072 *pCount = 0; 1073 fp = thread->curFrame; 1074 1075 assert(thread == dvmThreadSelf() || dvmIsSuspended(thread)); 1076 1077 /* 1078 * We're looking at a stack frame for code running below a Throwable 1079 * constructor. We want to remove the Throwable methods and the 1080 * superclass initializations so the user doesn't see them when they 1081 * read the stack dump. 1082 * 1083 * TODO: this just scrapes off the top layers of Throwable. Might not do 1084 * the right thing if we create an exception object or cause a VM 1085 * exception while in a Throwable method. 1086 */ 1087 while (fp != NULL) { 1088 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 1089 const Method* method = saveArea->method; 1090 1091 if (dvmIsBreakFrame((u4*)fp)) 1092 break; 1093 if (!dvmInstanceof(method->clazz, gDvm.exThrowable)) 1094 break; 1095 //LOGD("EXCEP: ignoring %s.%s\n", 1096 // method->clazz->descriptor, method->name); 1097 fp = saveArea->prevFrame; 1098 } 1099 startFp = fp; 1100 1101 /* 1102 * Compute the stack depth. 1103 */ 1104 stackDepth = 0; 1105 while (fp != NULL) { 1106 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 1107 1108 if (!dvmIsBreakFrame((u4*)fp)) 1109 stackDepth++; 1110 1111 assert(fp != saveArea->prevFrame); 1112 fp = saveArea->prevFrame; 1113 } 1114 //LOGD("EXCEP: stack depth is %d\n", stackDepth); 1115 1116 if (!stackDepth) 1117 goto bail; 1118 1119 /* 1120 * We need to store a pointer to the Method and the program counter. 1121 * We have 4-byte pointers, so we use '[I'. 1122 */ 1123 if (wantObject) { 1124 assert(sizeof(Method*) == 4); 1125 stackData = dvmAllocPrimitiveArray('I', stackDepth*2, ALLOC_DEFAULT); 1126 if (stackData == NULL) { 1127 assert(dvmCheckException(dvmThreadSelf())); 1128 goto bail; 1129 } 1130 intPtr = (int*) stackData->contents; 1131 } else { 1132 /* array of ints; first entry is stack depth */ 1133 assert(sizeof(Method*) == sizeof(int)); 1134 simpleData = (int*) malloc(sizeof(int) * stackDepth*2); 1135 if (simpleData == NULL) 1136 goto bail; 1137 1138 assert(pCount != NULL); 1139 intPtr = simpleData; 1140 } 1141 if (pCount != NULL) 1142 *pCount = stackDepth; 1143 1144 fp = startFp; 1145 while (fp != NULL) { 1146 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 1147 const Method* method = saveArea->method; 1148 1149 if (!dvmIsBreakFrame((u4*)fp)) { 1150 //LOGD("EXCEP keeping %s.%s\n", method->clazz->descriptor, 1151 // method->name); 1152 1153 *intPtr++ = (int) method; 1154 if (dvmIsNativeMethod(method)) { 1155 *intPtr++ = 0; /* no saved PC for native methods */ 1156 } else { 1157 assert(saveArea->xtra.currentPc >= method->insns && 1158 saveArea->xtra.currentPc < 1159 method->insns + dvmGetMethodInsnsSize(method)); 1160 *intPtr++ = (int) (saveArea->xtra.currentPc - method->insns); 1161 } 1162 1163 stackDepth--; // for verification 1164 } 1165 1166 assert(fp != saveArea->prevFrame); 1167 fp = saveArea->prevFrame; 1168 } 1169 assert(stackDepth == 0); 1170 1171bail: 1172 if (wantObject) { 1173 dvmReleaseTrackedAlloc((Object*) stackData, dvmThreadSelf()); 1174 return stackData; 1175 } else { 1176 return simpleData; 1177 } 1178} 1179 1180 1181/* 1182 * Given an Object previously created by dvmFillInStackTrace(), use the 1183 * contents of the saved stack trace to generate an array of 1184 * java/lang/StackTraceElement objects. 1185 * 1186 * The returned array is not added to the "local refs" list. 1187 */ 1188ArrayObject* dvmGetStackTrace(const Object* ostackData) 1189{ 1190 const ArrayObject* stackData = (const ArrayObject*) ostackData; 1191 const int* intVals; 1192 int stackSize; 1193 1194 stackSize = stackData->length / 2; 1195 intVals = (const int*) stackData->contents; 1196 return dvmGetStackTraceRaw(intVals, stackSize); 1197} 1198 1199/* 1200 * Generate an array of StackTraceElement objects from the raw integer 1201 * data encoded by dvmFillInStackTrace(). 1202 * 1203 * "intVals" points to the first {method,pc} pair. 1204 * 1205 * The returned array is not added to the "local refs" list. 1206 */ 1207ArrayObject* dvmGetStackTraceRaw(const int* intVals, int stackDepth) 1208{ 1209 ArrayObject* steArray = NULL; 1210 int i; 1211 1212 /* init this if we haven't yet */ 1213 if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement)) 1214 dvmInitClass(gDvm.classJavaLangStackTraceElement); 1215 1216 /* allocate a StackTraceElement array */ 1217 steArray = dvmAllocArray(gDvm.classJavaLangStackTraceElementArray, 1218 stackDepth, kObjectArrayRefWidth, ALLOC_DEFAULT); 1219 if (steArray == NULL) 1220 goto bail; 1221 1222 /* 1223 * Allocate and initialize a StackTraceElement for each stack frame. 1224 * We use the standard constructor to configure the object. 1225 */ 1226 for (i = 0; i < stackDepth; i++) { 1227 Object* ste; 1228 Method* meth; 1229 StringObject* className; 1230 StringObject* methodName; 1231 StringObject* fileName; 1232 int lineNumber, pc; 1233 const char* sourceFile; 1234 char* dotName; 1235 1236 ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT); 1237 if (ste == NULL) 1238 goto bail; 1239 1240 meth = (Method*) *intVals++; 1241 pc = *intVals++; 1242 1243 if (pc == -1) // broken top frame? 1244 lineNumber = 0; 1245 else 1246 lineNumber = dvmLineNumFromPC(meth, pc); 1247 1248 dotName = dvmHumanReadableDescriptor(meth->clazz->descriptor); 1249 className = dvmCreateStringFromCstr(dotName); 1250 free(dotName); 1251 1252 methodName = dvmCreateStringFromCstr(meth->name); 1253 sourceFile = dvmGetMethodSourceFile(meth); 1254 if (sourceFile != NULL) 1255 fileName = dvmCreateStringFromCstr(sourceFile); 1256 else 1257 fileName = NULL; 1258 1259 /* 1260 * Invoke: 1261 * public StackTraceElement(String declaringClass, String methodName, 1262 * String fileName, int lineNumber) 1263 * (where lineNumber==-2 means "native") 1264 */ 1265 JValue unused; 1266 dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init, 1267 ste, &unused, className, methodName, fileName, lineNumber); 1268 1269 dvmReleaseTrackedAlloc(ste, NULL); 1270 dvmReleaseTrackedAlloc((Object*) className, NULL); 1271 dvmReleaseTrackedAlloc((Object*) methodName, NULL); 1272 dvmReleaseTrackedAlloc((Object*) fileName, NULL); 1273 1274 if (dvmCheckException(dvmThreadSelf())) 1275 goto bail; 1276 1277 dvmSetObjectArrayElement(steArray, i, ste); 1278 } 1279 1280bail: 1281 dvmReleaseTrackedAlloc((Object*) steArray, NULL); 1282 return steArray; 1283} 1284 1285/* 1286 * Dump the contents of a raw stack trace to the log. 1287 */ 1288void dvmLogRawStackTrace(const int* intVals, int stackDepth) 1289{ 1290 int i; 1291 1292 /* 1293 * Run through the array of stack frame data. 1294 */ 1295 for (i = 0; i < stackDepth; i++) { 1296 Method* meth; 1297 int lineNumber, pc; 1298 const char* sourceFile; 1299 char* dotName; 1300 1301 meth = (Method*) *intVals++; 1302 pc = *intVals++; 1303 1304 if (pc == -1) // broken top frame? 1305 lineNumber = 0; 1306 else 1307 lineNumber = dvmLineNumFromPC(meth, pc); 1308 1309 // probably don't need to do this, but it looks nicer 1310 dotName = dvmHumanReadableDescriptor(meth->clazz->descriptor); 1311 1312 if (dvmIsNativeMethod(meth)) { 1313 LOGI("\tat %s.%s(Native Method)\n", dotName, meth->name); 1314 } else { 1315 LOGI("\tat %s.%s(%s:%d)\n", 1316 dotName, meth->name, dvmGetMethodSourceFile(meth), 1317 dvmLineNumFromPC(meth, pc)); 1318 } 1319 1320 free(dotName); 1321 1322 sourceFile = dvmGetMethodSourceFile(meth); 1323 } 1324} 1325 1326/* 1327 * Get the message string. We'd like to just grab the field out of 1328 * Throwable, but the getMessage() function can be overridden by the 1329 * sub-class. 1330 * 1331 * Returns the message string object, or NULL if it wasn't set or 1332 * we encountered a failure trying to retrieve it. The string will 1333 * be added to the tracked references table. 1334 */ 1335static StringObject* getExceptionMessage(Object* exception) 1336{ 1337 Thread* self = dvmThreadSelf(); 1338 Method* getMessageMethod; 1339 StringObject* messageStr = NULL; 1340 1341 assert(exception == self->exception); 1342 dvmAddTrackedAlloc(exception, self); 1343 self->exception = NULL; 1344 1345 getMessageMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz, 1346 "getMessage", "()Ljava/lang/String;"); 1347 if (getMessageMethod != NULL) { 1348 /* could be in NATIVE mode from CheckJNI, so switch state */ 1349 ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_RUNNING); 1350 JValue result; 1351 1352 dvmCallMethod(self, getMessageMethod, exception, &result); 1353 messageStr = (StringObject*) result.l; 1354 if (messageStr != NULL) 1355 dvmAddTrackedAlloc((Object*) messageStr, self); 1356 1357 dvmChangeStatus(self, oldStatus); 1358 } else { 1359 LOGW("WARNING: could not find getMessage in %s\n", 1360 exception->clazz->descriptor); 1361 } 1362 1363 if (self->exception != NULL) { 1364 LOGW("NOTE: exception thrown while retrieving exception message: %s\n", 1365 self->exception->clazz->descriptor); 1366 } 1367 1368 self->exception = exception; 1369 dvmReleaseTrackedAlloc(exception, self); 1370 return messageStr; 1371} 1372 1373/* 1374 * Print the direct stack trace of the given exception to the log. 1375 */ 1376static void logStackTraceOf(Object* exception) 1377{ 1378 const ArrayObject* stackData; 1379 StringObject* messageStr; 1380 int stackSize; 1381 const int* intVals; 1382 char* className; 1383 1384 className = dvmHumanReadableDescriptor(exception->clazz->descriptor); 1385 messageStr = getExceptionMessage(exception); 1386 if (messageStr != NULL) { 1387 char* cp = dvmCreateCstrFromString(messageStr); 1388 dvmReleaseTrackedAlloc((Object*) messageStr, dvmThreadSelf()); 1389 messageStr = NULL; 1390 1391 LOGI("%s: %s\n", className, cp); 1392 free(cp); 1393 } else { 1394 LOGI("%s:\n", className); 1395 } 1396 free(className); 1397 1398 /* 1399 * This relies on the stackState field, which contains the "raw" 1400 * form of the stack. The Throwable class may clear this field 1401 * after it generates the "cooked" form, in which case we'll have 1402 * nothing to show. 1403 */ 1404 stackData = (const ArrayObject*) dvmGetFieldObject(exception, 1405 gDvm.offJavaLangThrowable_stackState); 1406 if (stackData == NULL) { 1407 LOGI(" (raw stack trace not found)\n"); 1408 return; 1409 } 1410 1411 stackSize = stackData->length / 2; 1412 intVals = (const int*) stackData->contents; 1413 1414 dvmLogRawStackTrace(intVals, stackSize); 1415} 1416 1417/* 1418 * Print the stack trace of the current thread's exception, as well as 1419 * the stack traces of any chained exceptions, to the log. We extract 1420 * the stored stack trace and process it internally instead of calling 1421 * interpreted code. 1422 */ 1423void dvmLogExceptionStackTrace(void) 1424{ 1425 Object* exception = dvmThreadSelf()->exception; 1426 Object* cause; 1427 1428 if (exception == NULL) { 1429 LOGW("tried to log a null exception?\n"); 1430 return; 1431 } 1432 1433 for (;;) { 1434 logStackTraceOf(exception); 1435 cause = dvmGetExceptionCause(exception); 1436 if (cause == NULL) { 1437 break; 1438 } 1439 LOGI("Caused by:\n"); 1440 exception = cause; 1441 } 1442} 1443 1444void dvmThrowAbstractMethodError(const char* msg) { 1445 dvmThrowException("Ljava/lang/AbstractMethodError;", msg); 1446} 1447 1448void dvmThrowArithmeticException(const char* msg) { 1449 dvmThrowExceptionByClass(gDvm.exArithmeticException, msg); 1450} 1451 1452void dvmThrowArrayIndexOutOfBoundsException(int index, int length) 1453{ 1454 dvmThrowExceptionFmtByClass(gDvm.exArrayIndexOutOfBoundsException, 1455 "length=%d; index=%d", length, index); 1456} 1457 1458/* 1459 * Throw the indicated exception, with a message based on a format 1460 * in which "%s" is used exactly twice, first for a received class and 1461 * second for the expected class. 1462 */ 1463static void throwTypeError(ClassObject* exceptionClass, const char* fmt, 1464 ClassObject* actual, ClassObject* desired) 1465{ 1466 char* actualClassName = dvmHumanReadableDescriptor(actual->descriptor); 1467 char* desiredClassName = dvmHumanReadableDescriptor(desired->descriptor); 1468 dvmThrowExceptionFmtByClass(exceptionClass, fmt, 1469 actualClassName, desiredClassName); 1470 free(desiredClassName); 1471 free(actualClassName); 1472} 1473 1474void dvmThrowArrayStoreException(ClassObject* actual, ClassObject* desired) 1475{ 1476 throwTypeError(gDvm.exArrayStoreException, 1477 "%s cannot be stored in an array of type %s", 1478 actual, desired); 1479} 1480 1481void dvmThrowClassCastException(ClassObject* actual, ClassObject* desired) 1482{ 1483 throwTypeError(gDvm.exClassCastException, 1484 "%s cannot be cast to %s", actual, desired); 1485} 1486 1487void dvmThrowClassCircularityError(const char* descriptor) { 1488 dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCircularityError;", 1489 descriptor); 1490} 1491 1492void dvmThrowClassFormatError(const char* msg) { 1493 dvmThrowExceptionByClass(gDvm.exClassFormatError, msg); 1494} 1495 1496void dvmThrowClassNotFoundException(const char* name) { 1497 // TODO: Should the name be converted into human-readable form? 1498 dvmThrowExceptionByClass(gDvm.exClassNotFoundException, name); 1499} 1500 1501void dvmThrowChainedClassNotFoundException(const char* name, Object* cause) { 1502 // TODO: Should the name be converted into human-readable form? 1503 dvmThrowChainedExceptionByClass(gDvm.exClassNotFoundException, name, 1504 cause); 1505} 1506 1507void dvmThrowExceptionInInitializerError(void) 1508{ 1509 /* 1510 * TODO: Do we want to wrap it if the original is an Error rather than 1511 * an Exception? 1512 * 1513 * TODO: Should this just use dvmWrapException()? 1514 */ 1515 1516 if (gDvm.exExceptionInInitializerError == NULL) { 1517 /* 1518 * ExceptionInInitializerError isn't itself initialized. This 1519 * can happen very early during VM startup if there is a 1520 * problem with one of the corest-of-the-core classes, and it 1521 * can possibly happen during a dexopt run. Rather than do 1522 * anything fancier, we just abort here with a blatant 1523 * message. 1524 */ 1525 LOGE("Fatal error during early class initialization:\n"); 1526 dvmLogExceptionStackTrace(); 1527 dvmAbort(); 1528 } 1529 1530 Thread* self = dvmThreadSelf(); 1531 Object* exception = dvmGetException(self); 1532 1533 dvmAddTrackedAlloc(exception, self); 1534 dvmClearException(self); 1535 1536 dvmThrowChainedExceptionByClass(gDvm.exExceptionInInitializerError, 1537 NULL, exception); 1538 dvmReleaseTrackedAlloc(exception, self); 1539} 1540 1541void dvmThrowFileNotFoundException(const char* msg) { 1542 dvmThrowExceptionByClass(gDvm.exFileNotFoundException, msg); 1543} 1544 1545void dvmThrowIOException(const char* msg) { 1546 dvmThrowExceptionByClass(gDvm.exIOException, msg); 1547} 1548 1549void dvmThrowIllegalAccessException(const char* msg) { 1550 dvmThrowExceptionByClass(gDvm.exIllegalAccessException, msg); 1551} 1552 1553void dvmThrowIllegalAccessError(const char* msg) { 1554 dvmThrowException("Ljava/lang/IllegalAccessError;", msg); 1555} 1556 1557void dvmThrowIllegalArgumentException(const char* msg) { 1558 dvmThrowException("Ljava/lang/IllegalArgumentException;", msg); 1559} 1560 1561void dvmThrowIllegalMonitorStateException(const char* msg) { 1562 dvmThrowException("Ljava/lang/IllegalMonitorStateException;", msg); 1563} 1564 1565void dvmThrowIllegalStateException(const char* msg) { 1566 dvmThrowException("Ljava/lang/IllegalStateException;", msg); 1567} 1568 1569void dvmThrowIllegalThreadStateException(const char* msg) { 1570 dvmThrowException("Ljava/lang/IllegalThreadStateException;", msg); 1571} 1572 1573void dvmThrowIncompatibleClassChangeError(const char* msg) { 1574 dvmThrowException("Ljava/lang/IncompatibleClassChangeError;", msg); 1575} 1576 1577void dvmThrowIncompatibleClassChangeErrorWithClassMessage( 1578 const char* descriptor) 1579{ 1580 dvmThrowExceptionWithClassMessage( 1581 "Ljava/lang/IncompatibleClassChangeError;", descriptor); 1582} 1583 1584void dvmThrowInternalError(const char* msg) { 1585 dvmThrowException("Ljava/lang/InternalError;", msg); 1586} 1587 1588void dvmThrowInterruptedException(const char* msg) { 1589 dvmThrowExceptionByClass(gDvm.exInterruptedException, msg); 1590} 1591 1592void dvmThrowLinkageError(const char* msg) { 1593 dvmThrowException("Ljava/lang/LinkageError;", msg); 1594} 1595 1596void dvmThrowNegativeArraySizeException(s4 size) { 1597 dvmThrowExceptionFmtByClass(gDvm.exNegativeArraySizeException, "%d", size); 1598} 1599 1600void dvmThrowNoClassDefFoundError(const char* descriptor) { 1601 dvmThrowExceptionWithClassMessage("Ljava/lang/NoClassDefFoundError;", 1602 descriptor); 1603} 1604 1605void dvmThrowNoSuchFieldError(const char* msg) { 1606 dvmThrowException("Ljava/lang/NoSuchFieldError;", msg); 1607} 1608 1609void dvmThrowNoSuchFieldException(const char* msg) { 1610 dvmThrowExceptionByClass(gDvm.exNoSuchFieldException, msg); 1611} 1612 1613void dvmThrowNoSuchMethodError(const char* msg) { 1614 dvmThrowException("Ljava/lang/NoSuchMethodError;", msg); 1615} 1616 1617void dvmThrowNullPointerException(const char* msg) { 1618 dvmThrowExceptionByClass(gDvm.exNullPointerException, msg); 1619} 1620 1621void dvmThrowOutOfMemoryError(const char* msg) { 1622 dvmThrowException("Ljava/lang/OutOfMemoryError;", msg); 1623} 1624 1625void dvmThrowRuntimeException(const char* msg) { 1626 dvmThrowExceptionByClass(gDvm.exRuntimeException, msg); 1627} 1628 1629void dvmThrowStaleDexCacheError(const char* msg) { 1630 dvmThrowExceptionByClass(gDvm.exStaleDexCacheError, msg); 1631} 1632 1633void dvmThrowStringIndexOutOfBoundsExceptionWithIndex(jsize stringLength, 1634 jsize requestIndex) { 1635 dvmThrowExceptionFmtByClass(gDvm.exStringIndexOutOfBoundsException, 1636 "length=%d; index=%d", stringLength, requestIndex); 1637} 1638 1639void dvmThrowStringIndexOutOfBoundsExceptionWithRegion(jsize stringLength, 1640 jsize requestStart, jsize requestLength) { 1641 dvmThrowExceptionFmtByClass(gDvm.exStringIndexOutOfBoundsException, 1642 "length=%d; regionStart=%d regionLength=%d", 1643 stringLength, requestStart, requestLength); 1644} 1645 1646void dvmThrowUnsatisfiedLinkError(const char* msg) { 1647 dvmThrowException("Ljava/lang/UnsatisfiedLinkError;", msg); 1648} 1649 1650void dvmThrowUnsupportedOperationException(const char* msg) { 1651 dvmThrowExceptionByClass(gDvm.exUnsupportedOperationException, msg); 1652} 1653 1654void dvmThrowVirtualMachineError(const char* msg) { 1655 dvmThrowExceptionByClass(gDvm.exVirtualMachineError, msg); 1656} 1657