2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 #include "libdex/DexCatch.h"
25 Notes on Exception Handling
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.
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
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.
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.
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.
58 There are a few ways to deal with this:
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.
72 Solution (a) is the simplest, but breaks Dalvik's goal of mingling native
73 and interpreted stacks.
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.
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
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
94 Current plan is option (b), because it's simple, fast, and doesn't change
95 the way the stack works.
99 static bool initException(Object* exception, const char* msg, Object* cause,
104 * Cache pointers to some of the exception classes we use locally.
106 * Note this is NOT called during dexopt optimization. Some of the fields
107 * are initialized by the verifier (dvmVerifyCodeFlow).
109 bool dvmExceptionStartup(void)
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)
127 LOGE("Could not find one or more essential exception classes\n");
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.)
138 meth = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangStackTraceElement,
139 "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
141 LOGE("Unable to find constructor for StackTraceElement\n");
144 gDvm.methJavaLangStackTraceElement_init = meth;
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");
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");
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");
179 void dvmExceptionShutdown(void)
186 * Format the message into a small buffer and pass it along.
188 void dvmThrowExceptionFmtV(const char* exceptionDescriptor, const char* fmt,
193 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
194 dvmThrowChainedException(exceptionDescriptor, msgBuf, NULL);
198 * Create a Throwable and throw an exception in the current thread (where
199 * "throwing" just means "set the thread's exception pointer").
201 * "msg" and/or "cause" may be NULL.
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.
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.
215 * [Do we want to cache pointers to common exception classes?]
217 void dvmThrowChainedException(const char* exceptionDescriptor, const char* msg,
220 ClassObject* excepClass;
222 LOGV("THROW '%s' msg='%s' cause=%s\n",
223 exceptionDescriptor, msg,
224 (cause != NULL) ? cause->clazz->descriptor : "(none)");
226 if (gDvm.initializing) {
227 if (++gDvm.initExceptionCount >= 2) {
228 LOGE("Too many exceptions during init (failed on '%s' '%s')\n",
229 exceptionDescriptor, msg);
234 excepClass = dvmFindSystemClass(exceptionDescriptor);
235 if (excepClass == NULL) {
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.
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"
248 if (!dvmCheckException(dvmThreadSelf())) {
249 LOGE("FATAL: unable to throw exception (failed on '%s' '%s')\n",
250 exceptionDescriptor, msg);
256 dvmThrowChainedExceptionByClass(excepClass, msg, cause);
260 * Start/continue throwing process now that we have a class reference.
262 void dvmThrowChainedExceptionByClass(ClassObject* excepClass, const char* msg,
265 Thread* self = dvmThreadSelf();
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)
274 dvmThrowChainedException("Ljava/lang/InternalError;",
275 "failed to init original exception class", cause);
279 exception = dvmAllocObject(excepClass, ALLOC_DEFAULT);
280 if (exception == NULL) {
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.
289 if (dvmCheckException(self))
291 LOGE("FATAL: unable to allocate exception '%s' '%s'\n",
292 excepClass->descriptor, msg != NULL ? msg : "(no msg)");
297 * Init the exception.
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);
304 assert(excepClass == exception->clazz);
305 if (!initException(exception, msg, cause, self)) {
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.
311 if (!dvmCheckException(self)) {
313 * We're required to throw something, so we just
314 * throw the pre-constructed internal error.
316 self->exception = gDvm.internalErrorObj;
322 self->exception = exception;
325 dvmReleaseTrackedAlloc(exception, self);
329 * Throw the named exception using the dotted form of the class
330 * descriptor as the exception message, and with the specified cause.
332 void dvmThrowChainedExceptionWithClassMessage(const char* exceptionDescriptor,
333 const char* messageDescriptor, Object* cause)
335 char* message = dvmDescriptorToDot(messageDescriptor);
337 dvmThrowChainedException(exceptionDescriptor, message, cause);
342 * Like dvmThrowExceptionWithMessageFromDescriptor, but take a
343 * class object instead of a name.
345 void dvmThrowExceptionByClassWithClassMessage(ClassObject* exceptionClass,
346 const char* messageDescriptor)
348 char* message = dvmDescriptorToName(messageDescriptor);
350 dvmThrowExceptionByClass(exceptionClass, message);
355 * Find and return an exception constructor method that can take the
356 * indicated parameters, or return NULL if no such constructor exists.
358 static Method* findExceptionInitMethod(ClassObject* excepClass,
359 bool hasMessage, bool hasCause)
365 result = dvmFindDirectMethodByDescriptor(
366 excepClass, "<init>",
367 "(Ljava/lang/String;Ljava/lang/Throwable;)V");
369 result = dvmFindDirectMethodByDescriptor(
370 excepClass, "<init>", "(Ljava/lang/String;)V");
373 if (result != NULL) {
378 return dvmFindDirectMethodByDescriptor(
379 excepClass, "<init>",
380 "(Ljava/lang/Object;Ljava/lang/Throwable;)V");
382 return dvmFindDirectMethodByDescriptor(
383 excepClass, "<init>", "(Ljava/lang/Object;)V");
385 } else if (hasCause) {
386 return dvmFindDirectMethodByDescriptor(
387 excepClass, "<init>", "(Ljava/lang/Throwable;)V");
389 return dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V");
394 * Initialize an exception with an appropriate constructor.
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.
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.
404 static bool initException(Object* exception, const char* msg, Object* cause,
413 } initKind = kInitUnknown;
414 Method* initMethod = NULL;
415 ClassObject* excepClass = exception->clazz;
416 StringObject* msgStr = NULL;
418 bool needInitCause = false;
420 assert(self != NULL);
421 assert(self->exception == NULL);
423 /* if we have a message, create a String */
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);
437 if (!dvmInstanceof(cause->clazz, gDvm.classJavaLangThrowable)) {
438 LOGE("Tried to init exception with cause '%s'\n",
439 cause->clazz->descriptor);
445 * The Throwable class has four public constructors:
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)
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.
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().
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
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.)
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
482 if (msgStr == NULL) {
483 initMethod = findExceptionInitMethod(excepClass, false, false);
484 initKind = kInitNoarg;
486 initMethod = findExceptionInitMethod(excepClass, true, false);
487 if (initMethod != NULL) {
491 initMethod = findExceptionInitMethod(excepClass, true, true);
492 if (initMethod != NULL) {
493 initKind = kInitMsgThrow;
498 if (msgStr == NULL) {
499 initMethod = findExceptionInitMethod(excepClass, false, true);
500 if (initMethod != NULL) {
501 initKind = kInitThrow;
503 initMethod = findExceptionInitMethod(excepClass, false, false);
504 initKind = kInitNoarg;
505 needInitCause = true;
508 initMethod = findExceptionInitMethod(excepClass, true, true);
509 if (initMethod != NULL) {
510 initKind = kInitMsgThrow;
512 initMethod = findExceptionInitMethod(excepClass, true, false);
514 needInitCause = true;
519 if (initMethod == NULL) {
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.
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);
537 * Call the constructor with the appropriate arguments.
542 LOGVV("+++ exc noarg (ic=%d)\n", needInitCause);
543 dvmCallMethod(self, initMethod, exception, &unused);
546 LOGVV("+++ exc msg (ic=%d)\n", needInitCause);
547 dvmCallMethod(self, initMethod, exception, &unused, msgStr);
550 LOGVV("+++ exc throw");
551 assert(!needInitCause);
552 dvmCallMethod(self, initMethod, exception, &unused, cause);
555 LOGVV("+++ exc msg+throw");
556 assert(!needInitCause);
557 dvmCallMethod(self, initMethod, exception, &unused, msgStr, cause);
565 * It's possible the constructor has thrown an exception. If so, we
566 * return an error and let our caller deal with it.
568 if (self->exception != NULL) {
569 LOGW("Exception thrown (%s) while throwing internal exception (%s)\n",
570 self->exception->clazz->descriptor, exception->clazz->descriptor);
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.
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.
589 LOGW("Exception thrown (%s) during initCause() "
590 "of internal exception (%s)\n",
591 self->exception->clazz->descriptor,
592 exception->clazz->descriptor);
596 LOGW("WARNING: couldn't find initCause in '%s'\n",
597 excepClass->descriptor);
605 dvmReleaseTrackedAlloc((Object*) msgStr, self); // NULL is ok
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.
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.
620 void dvmClearOptException(Thread* self)
622 self->exception = NULL;
623 gDvm.initExceptionCount = 0;
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.
630 bool dvmIsCheckedException(const Object* exception)
632 if (dvmInstanceof(exception->clazz, gDvm.classJavaLangError) ||
633 dvmInstanceof(exception->clazz, gDvm.classJavaLangRuntimeException))
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.
646 * If something fails, an (unchecked) exception related to that failure
647 * will be pending instead.
649 void dvmWrapException(const char* newExcepStr)
651 Thread* self = dvmThreadSelf();
653 ClassObject* iteClass;
655 origExcep = dvmGetException(self);
656 dvmAddTrackedAlloc(origExcep, self); // don't let the GC free it
658 dvmClearException(self); // clear before class lookup
659 iteClass = dvmFindSystemClass(newExcepStr);
660 if (iteClass != NULL) {
664 iteExcep = dvmAllocObject(iteClass, ALLOC_DEFAULT);
665 if (iteExcep != NULL) {
666 initMethod = dvmFindDirectMethodByDescriptor(iteClass, "<init>",
667 "(Ljava/lang/Throwable;)V");
668 if (initMethod != NULL) {
670 dvmCallMethod(self, initMethod, iteExcep, &unused,
673 /* if <init> succeeded, replace the old exception */
674 if (!dvmCheckException(self))
675 dvmSetException(self, iteExcep);
677 dvmReleaseTrackedAlloc(iteExcep, NULL);
679 /* if initMethod doesn't exist, or failed... */
680 if (!dvmCheckException(self))
681 dvmSetException(self, origExcep);
683 /* leave OutOfMemoryError pending */
686 /* leave ClassNotFoundException pending */
689 assert(dvmCheckException(self));
690 dvmReleaseTrackedAlloc(origExcep, self);
694 * Get the "cause" field from an exception.
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.
700 Object* dvmGetExceptionCause(const Object* exception)
702 if (!dvmInstanceof(exception->clazz, gDvm.classJavaLangThrowable)) {
703 LOGE("Tried to get cause from object of type '%s'\n",
704 exception->clazz->descriptor);
708 dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause);
709 if (cause == exception)
716 * Print the stack trace of the current exception on stderr. This is called
717 * from the JNI ExceptionDescribe call.
719 * For consistency we just invoke the Throwable printStackTrace method,
720 * which might be overridden in the exception object.
722 * Exceptions thrown during the course of printing the stack trace are
725 void dvmPrintExceptionStackTrace(void)
727 Thread* self = dvmThreadSelf();
731 exception = self->exception;
732 if (exception == NULL)
735 self->exception = NULL;
736 printMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz,
737 "printStackTrace", "()V");
738 if (printMethod != NULL) {
740 dvmCallMethod(self, printMethod, exception, &unused);
742 LOGW("WARNING: could not find printStackTrace in %s\n",
743 exception->clazz->descriptor);
746 if (self->exception != NULL) {
747 LOGI("NOTE: exception thrown while printing stack trace: %s\n",
748 self->exception->clazz->descriptor);
751 self->exception = exception;
755 * Search the method's list of exceptions for a match.
757 * Returns the offset of the catch block on success, or -1 on failure.
759 static int findCatchInMethod(Thread* self, const Method* method, int relPc,
760 ClassObject* excepClass)
763 * Need to clear the exception before entry. Otherwise, dvmResolveClass
764 * might think somebody threw an exception while it was loading a class.
766 assert(!dvmCheckException(self));
767 assert(!dvmIsNativeMethod(method));
769 LOGVV("findCatchInMethod %s.%s excep=%s depth=%d\n",
770 method->clazz->descriptor, method->name, excepClass->descriptor,
771 dvmComputeExactFrameDepth(self->curFrame));
773 DvmDex* pDvmDex = method->clazz->pDvmDex;
774 const DexCode* pCode = dvmGetMethodCode(method);
775 DexCatchIterator iterator;
777 if (dexFindCatchHandler(&iterator, pCode, relPc)) {
779 DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
781 if (handler == NULL) {
785 if (handler->typeIdx == kDexNoIndex) {
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;
793 ClassObject* throwable =
794 dvmDexGetResolvedClass(pDvmDex, handler->typeIdx);
795 if (throwable == NULL) {
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.
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"
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
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.
823 throwable = dvmResolveClass(method->clazz, handler->typeIdx,
825 if (throwable == NULL) {
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.
833 LOGW("Could not resolve class ref'ed in exception "
834 "catch list (class index %d, exception %s)\n",
836 (self->exception != NULL) ?
837 self->exception->clazz->descriptor : "(none)");
838 dvmClearException(self);
843 //LOGD("ADDR MATCH, check %s instanceof %s\n",
844 // excepClass->descriptor, pEntry->excepClass->descriptor);
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;
855 LOGV("No matching catch block at 0x%02x in %s for %s\n",
856 relPc, method->name, excepClass->descriptor);
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.
864 * Returns the offset to the catch block, or -1 if we run up against a
865 * break frame without finding anything.
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.
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.
874 int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
875 bool scanOnly, void** newFrame)
877 void* fp = self->curFrame;
880 assert(!dvmCheckException(self));
883 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
884 catchAddr = findCatchInMethod(self, saveArea->method, relPc,
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.
895 /* output method profiling info */
897 TRACE_METHOD_UNROLL(self, saveArea->method);
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.
906 assert(saveArea->prevFrame != NULL);
907 if (dvmIsBreakFrame(saveArea->prevFrame)) {
909 break; // bail with catchAddr == -1
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.
918 * Start by finding an interpreted code frame.
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
924 if (!dvmIsBreakFrame(fp)) {
925 saveArea = SAVEAREA_FROM_FP(fp);
926 if (!dvmIsNativeMethod(saveArea->method))
930 fp = SAVEAREA_FROM_FP(fp)->prevFrame;
933 break; // bail with catchAddr == -1
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.
941 saveArea->xtra.currentPc - SAVEAREA_FROM_FP(fp)->method->insns;
943 fp = saveArea->prevFrame;
945 /* savedPc in was-current frame goes with method in now-current */
946 relPc = saveArea->savedPc - SAVEAREA_FROM_FP(fp)->method->insns;
954 * The class resolution in findCatchInMethod() could cause an exception.
955 * Clear it to be safe.
957 self->exception = NULL;
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.
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
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.
979 * NOTE: if we support class unloading, we will need to scan the class
980 * object references out of these arrays.
982 void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, int* pCount)
984 ArrayObject* stackData = NULL;
985 int* simpleData = NULL;
993 fp = thread->curFrame;
995 assert(thread == dvmThreadSelf() || dvmIsSuspended(thread));
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.
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.
1007 while (fp != NULL) {
1008 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1009 const Method* method = saveArea->method;
1011 if (dvmIsBreakFrame(fp))
1013 if (!dvmInstanceof(method->clazz, gDvm.classJavaLangThrowable))
1015 //LOGD("EXCEP: ignoring %s.%s\n",
1016 // method->clazz->descriptor, method->name);
1017 fp = saveArea->prevFrame;
1022 * Compute the stack depth.
1025 while (fp != NULL) {
1026 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1028 if (!dvmIsBreakFrame(fp))
1031 assert(fp != saveArea->prevFrame);
1032 fp = saveArea->prevFrame;
1034 //LOGD("EXCEP: stack depth is %d\n", stackDepth);
1040 * We need to store a pointer to the Method and the program counter.
1041 * We have 4-byte pointers, so we use '[I'.
1044 assert(sizeof(Method*) == 4);
1045 stackData = dvmAllocPrimitiveArray('I', stackDepth*2, ALLOC_DEFAULT);
1046 if (stackData == NULL) {
1047 assert(dvmCheckException(dvmThreadSelf()));
1050 intPtr = (int*) stackData->contents;
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)
1058 assert(pCount != NULL);
1059 intPtr = simpleData;
1062 *pCount = stackDepth;
1065 while (fp != NULL) {
1066 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1067 const Method* method = saveArea->method;
1069 if (!dvmIsBreakFrame(fp)) {
1070 //LOGD("EXCEP keeping %s.%s\n", method->clazz->descriptor,
1073 *intPtr++ = (int) method;
1074 if (dvmIsNativeMethod(method)) {
1075 *intPtr++ = 0; /* no saved PC for native methods */
1077 assert(saveArea->xtra.currentPc >= method->insns &&
1078 saveArea->xtra.currentPc <
1079 method->insns + dvmGetMethodInsnsSize(method));
1080 *intPtr++ = (int) (saveArea->xtra.currentPc - method->insns);
1083 stackDepth--; // for verification
1086 assert(fp != saveArea->prevFrame);
1087 fp = saveArea->prevFrame;
1089 assert(stackDepth == 0);
1093 dvmReleaseTrackedAlloc((Object*) stackData, dvmThreadSelf());
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.
1106 * The returned array is not added to the "local refs" list.
1108 ArrayObject* dvmGetStackTrace(const Object* ostackData)
1110 const ArrayObject* stackData = (const ArrayObject*) ostackData;
1114 stackSize = stackData->length / 2;
1115 intVals = (const int*) stackData->contents;
1116 return dvmGetStackTraceRaw(intVals, stackSize);
1120 * Generate an array of StackTraceElement objects from the raw integer
1121 * data encoded by dvmFillInStackTrace().
1123 * "intVals" points to the first {method,pc} pair.
1125 * The returned array is not added to the "local refs" list.
1127 ArrayObject* dvmGetStackTraceRaw(const int* intVals, int stackDepth)
1129 ArrayObject* steArray = NULL;
1133 /* init this if we haven't yet */
1134 if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement))
1135 dvmInitClass(gDvm.classJavaLangStackTraceElement);
1137 /* allocate a StackTraceElement array */
1138 steArray = dvmAllocArray(gDvm.classJavaLangStackTraceElementArray,
1139 stackDepth, kObjectArrayRefWidth, ALLOC_DEFAULT);
1140 if (steArray == NULL)
1142 stePtr = (Object**) steArray->contents;
1145 * Allocate and initialize a StackTraceElement for each stack frame.
1146 * We use the standard constructor to configure the object.
1148 for (i = 0; i < stackDepth; i++) {
1151 StringObject* className;
1152 StringObject* methodName;
1153 StringObject* fileName;
1155 const char* sourceFile;
1158 ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT);
1162 meth = (Method*) *intVals++;
1165 if (pc == -1) // broken top frame?
1168 lineNumber = dvmLineNumFromPC(meth, pc);
1170 dotName = dvmDescriptorToDot(meth->clazz->descriptor);
1171 className = dvmCreateStringFromCstr(dotName, ALLOC_DEFAULT);
1174 methodName = dvmCreateStringFromCstr(meth->name, ALLOC_DEFAULT);
1175 sourceFile = dvmGetMethodSourceFile(meth);
1176 if (sourceFile != NULL)
1177 fileName = dvmCreateStringFromCstr(sourceFile, ALLOC_DEFAULT);
1183 * public StackTraceElement(String declaringClass, String methodName,
1184 * String fileName, int lineNumber)
1185 * (where lineNumber==-2 means "native")
1188 dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init,
1189 ste, &unused, className, methodName, fileName, lineNumber);
1191 dvmReleaseTrackedAlloc(ste, NULL);
1192 dvmReleaseTrackedAlloc((Object*) className, NULL);
1193 dvmReleaseTrackedAlloc((Object*) methodName, NULL);
1194 dvmReleaseTrackedAlloc((Object*) fileName, NULL);
1196 if (dvmCheckException(dvmThreadSelf()))
1203 dvmReleaseTrackedAlloc((Object*) steArray, NULL);
1208 * Dump the contents of a raw stack trace to the log.
1210 void dvmLogRawStackTrace(const int* intVals, int stackDepth)
1215 * Run through the array of stack frame data.
1217 for (i = 0; i < stackDepth; i++) {
1220 const char* sourceFile;
1223 meth = (Method*) *intVals++;
1226 if (pc == -1) // broken top frame?
1229 lineNumber = dvmLineNumFromPC(meth, pc);
1231 // probably don't need to do this, but it looks nicer
1232 dotName = dvmDescriptorToDot(meth->clazz->descriptor);
1234 if (dvmIsNativeMethod(meth)) {
1235 LOGI("\tat %s.%s(Native Method)\n", dotName, meth->name);
1237 LOGI("\tat %s.%s(%s:%d)\n",
1238 dotName, meth->name, dvmGetMethodSourceFile(meth),
1239 dvmLineNumFromPC(meth, pc));
1244 sourceFile = dvmGetMethodSourceFile(meth);
1249 * Print the direct stack trace of the given exception to the log.
1251 static void logStackTraceOf(Object* exception)
1253 const ArrayObject* stackData;
1254 StringObject* messageStr;
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);
1265 LOGI("%s:\n", exception->clazz->descriptor);
1268 stackData = (const ArrayObject*) dvmGetFieldObject(exception,
1269 gDvm.offJavaLangThrowable_stackState);
1270 if (stackData == NULL) {
1271 LOGI(" (no stack trace data found)\n");
1275 stackSize = stackData->length / 2;
1276 intVals = (const int*) stackData->contents;
1278 dvmLogRawStackTrace(intVals, stackSize);
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
1287 void dvmLogExceptionStackTrace(void)
1289 Object* exception = dvmThreadSelf()->exception;
1292 if (exception == NULL) {
1293 LOGW("tried to log a null exception?\n");
1298 logStackTraceOf(exception);
1299 cause = dvmGetExceptionCause(exception);
1300 if (cause == NULL) {
1303 LOGI("Caused by:\n");