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