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