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