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