| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 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 | */ |
| Andy McFadden | 4fbba1f | 2010-02-03 07:21:14 -0800 | [diff] [blame] | 16 | |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 17 | /* |
| 18 | * Stacks and their uses (e.g. native --> interpreted method calls). |
| 19 | * |
| 20 | * See the majestic ASCII art in Stack.h. |
| 21 | */ |
| 22 | #include "Dalvik.h" |
| 23 | #include "jni.h" |
| 24 | |
| 25 | #include <stdlib.h> |
| 26 | #include <stdarg.h> |
| 27 | |
| 28 | /* |
| 29 | * Initialize the interpreter stack in a new thread. |
| 30 | * |
| 31 | * Currently this doesn't do much, since we don't need to zero out the |
| 32 | * stack (and we really don't want to if it was created with mmap). |
| 33 | */ |
| 34 | bool dvmInitInterpStack(Thread* thread, int stackSize) |
| 35 | { |
| 36 | assert(thread->interpStackStart != NULL); |
| 37 | |
| 38 | assert(thread->curFrame == NULL); |
| 39 | |
| 40 | return true; |
| 41 | } |
| 42 | |
| 43 | /* |
| 44 | * We're calling an interpreted method from an internal VM function or |
| 45 | * via reflection. |
| 46 | * |
| 47 | * Push a frame for an interpreted method onto the stack. This is only |
| 48 | * used when calling into interpreted code from native code. (The |
| 49 | * interpreter does its own stack frame manipulation for interp-->interp |
| 50 | * calls.) |
| 51 | * |
| 52 | * The size we need to reserve is the sum of parameters, local variables, |
| 53 | * saved goodies, and outbound parameters. |
| 54 | * |
| 55 | * We start by inserting a "break" frame, which ensures that the interpreter |
| 56 | * hands control back to us after the function we call returns or an |
| 57 | * uncaught exception is thrown. |
| 58 | */ |
| 59 | static bool dvmPushInterpFrame(Thread* self, const Method* method) |
| 60 | { |
| 61 | StackSaveArea* saveBlock; |
| 62 | StackSaveArea* breakSaveBlock; |
| 63 | int stackReq; |
| 64 | u1* stackPtr; |
| 65 | |
| 66 | assert(!dvmIsNativeMethod(method)); |
| 67 | assert(!dvmIsAbstractMethod(method)); |
| 68 | |
| 69 | stackReq = method->registersSize * 4 // params + locals |
| 70 | + sizeof(StackSaveArea) * 2 // break frame + regular frame |
| 71 | + method->outsSize * 4; // args to other methods |
| 72 | |
| 73 | if (self->curFrame != NULL) |
| 74 | stackPtr = (u1*) SAVEAREA_FROM_FP(self->curFrame); |
| 75 | else |
| 76 | stackPtr = self->interpStackStart; |
| 77 | |
| 78 | if (stackPtr - stackReq < self->interpStackEnd) { |
| 79 | /* not enough space */ |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 80 | LOGW("Stack overflow on call to interp " |
| 81 | "(req=%d top=%p cur=%p size=%d %s.%s)\n", |
| 82 | stackReq, self->interpStackStart, self->curFrame, |
| 83 | self->interpStackSize, method->clazz->descriptor, method->name); |
| Andy McFadden | 6ed1a0f | 2009-09-10 15:34:19 -0700 | [diff] [blame] | 84 | dvmHandleStackOverflow(self, method); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 85 | assert(dvmCheckException(self)); |
| 86 | return false; |
| 87 | } |
| 88 | |
| 89 | /* |
| 90 | * Shift the stack pointer down, leaving space for the function's |
| 91 | * args/registers and save area. |
| 92 | */ |
| 93 | stackPtr -= sizeof(StackSaveArea); |
| 94 | breakSaveBlock = (StackSaveArea*)stackPtr; |
| 95 | stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea); |
| 96 | saveBlock = (StackSaveArea*) stackPtr; |
| 97 | |
| 98 | #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) |
| 99 | /* debug -- memset the new stack, unless we want valgrind's help */ |
| 100 | memset(stackPtr - (method->outsSize*4), 0xaf, stackReq); |
| 101 | #endif |
| 102 | #ifdef EASY_GDB |
| 103 | breakSaveBlock->prevSave = FP_FROM_SAVEAREA(self->curFrame); |
| 104 | saveBlock->prevSave = breakSaveBlock; |
| 105 | #endif |
| 106 | |
| 107 | breakSaveBlock->prevFrame = self->curFrame; |
| 108 | breakSaveBlock->savedPc = NULL; // not required |
| Andy McFadden | d5ab726 | 2009-08-25 07:19:34 -0700 | [diff] [blame] | 109 | breakSaveBlock->xtra.localRefCookie = 0; // not required |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 110 | breakSaveBlock->method = NULL; |
| 111 | saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock); |
| 112 | saveBlock->savedPc = NULL; // not required |
| 113 | saveBlock->xtra.currentPc = NULL; // not required? |
| 114 | saveBlock->method = method; |
| 115 | |
| 116 | LOGVV("PUSH frame: old=%p new=%p (size=%d)\n", |
| 117 | self->curFrame, FP_FROM_SAVEAREA(saveBlock), |
| 118 | (u1*)self->curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); |
| 119 | |
| 120 | self->curFrame = FP_FROM_SAVEAREA(saveBlock); |
| 121 | |
| 122 | return true; |
| 123 | } |
| 124 | |
| 125 | /* |
| 126 | * We're calling a JNI native method from an internal VM fuction or |
| 127 | * via reflection. This is also used to create the "fake" native-method |
| 128 | * frames at the top of the interpreted stack. |
| 129 | * |
| 130 | * This actually pushes two frames; the first is a "break" frame. |
| 131 | * |
| 132 | * The top frame has additional space for JNI local reference tracking. |
| 133 | */ |
| 134 | bool dvmPushJNIFrame(Thread* self, const Method* method) |
| 135 | { |
| 136 | StackSaveArea* saveBlock; |
| 137 | StackSaveArea* breakSaveBlock; |
| 138 | int stackReq; |
| 139 | u1* stackPtr; |
| 140 | |
| 141 | assert(dvmIsNativeMethod(method)); |
| 142 | |
| 143 | stackReq = method->registersSize * 4 // params only |
| 144 | + sizeof(StackSaveArea) * 2; // break frame + regular frame |
| 145 | |
| 146 | if (self->curFrame != NULL) |
| 147 | stackPtr = (u1*) SAVEAREA_FROM_FP(self->curFrame); |
| 148 | else |
| 149 | stackPtr = self->interpStackStart; |
| 150 | |
| 151 | if (stackPtr - stackReq < self->interpStackEnd) { |
| 152 | /* not enough space */ |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 153 | LOGW("Stack overflow on call to native " |
| 154 | "(req=%d top=%p cur=%p size=%d '%s')\n", |
| 155 | stackReq, self->interpStackStart, self->curFrame, |
| 156 | self->interpStackSize, method->name); |
| Andy McFadden | 6ed1a0f | 2009-09-10 15:34:19 -0700 | [diff] [blame] | 157 | dvmHandleStackOverflow(self, method); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 158 | assert(dvmCheckException(self)); |
| 159 | return false; |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | * Shift the stack pointer down, leaving space for just the stack save |
| 164 | * area for the break frame, then shift down farther for the full frame. |
| 165 | * We leave space for the method args, which are copied in later. |
| 166 | */ |
| 167 | stackPtr -= sizeof(StackSaveArea); |
| 168 | breakSaveBlock = (StackSaveArea*)stackPtr; |
| 169 | stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea); |
| 170 | saveBlock = (StackSaveArea*) stackPtr; |
| 171 | |
| 172 | #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) |
| 173 | /* debug -- memset the new stack */ |
| 174 | memset(stackPtr, 0xaf, stackReq); |
| 175 | #endif |
| 176 | #ifdef EASY_GDB |
| 177 | if (self->curFrame == NULL) |
| 178 | breakSaveBlock->prevSave = NULL; |
| 179 | else |
| 180 | breakSaveBlock->prevSave = FP_FROM_SAVEAREA(self->curFrame); |
| 181 | saveBlock->prevSave = breakSaveBlock; |
| 182 | #endif |
| 183 | |
| 184 | breakSaveBlock->prevFrame = self->curFrame; |
| 185 | breakSaveBlock->savedPc = NULL; // not required |
| Andy McFadden | d5ab726 | 2009-08-25 07:19:34 -0700 | [diff] [blame] | 186 | breakSaveBlock->xtra.localRefCookie = 0; // not required |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 187 | breakSaveBlock->method = NULL; |
| 188 | saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock); |
| 189 | saveBlock->savedPc = NULL; // not required |
| Andy McFadden | d5ab726 | 2009-08-25 07:19:34 -0700 | [diff] [blame] | 190 | #ifdef USE_INDIRECT_REF |
| 191 | saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all; |
| 192 | #else |
| 193 | saveBlock->xtra.localRefCookie = self->jniLocalRefTable.nextEntry; |
| 194 | #endif |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 195 | saveBlock->method = method; |
| 196 | |
| 197 | LOGVV("PUSH JNI frame: old=%p new=%p (size=%d)\n", |
| 198 | self->curFrame, FP_FROM_SAVEAREA(saveBlock), |
| 199 | (u1*)self->curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); |
| 200 | |
| 201 | self->curFrame = FP_FROM_SAVEAREA(saveBlock); |
| 202 | |
| 203 | return true; |
| 204 | } |
| 205 | |
| 206 | /* |
| 207 | * This is used by the JNI PushLocalFrame call. We push a new frame onto |
| 208 | * the stack that has no ins, outs, or locals, and no break frame above it. |
| 209 | * It's strictly used for tracking JNI local refs, and will be popped off |
| 210 | * by dvmPopFrame if it's not removed explicitly. |
| 211 | */ |
| 212 | bool dvmPushLocalFrame(Thread* self, const Method* method) |
| 213 | { |
| 214 | StackSaveArea* saveBlock; |
| 215 | int stackReq; |
| 216 | u1* stackPtr; |
| 217 | |
| 218 | assert(dvmIsNativeMethod(method)); |
| 219 | |
| 220 | stackReq = sizeof(StackSaveArea); // regular frame |
| 221 | |
| 222 | assert(self->curFrame != NULL); |
| 223 | stackPtr = (u1*) SAVEAREA_FROM_FP(self->curFrame); |
| 224 | |
| 225 | if (stackPtr - stackReq < self->interpStackEnd) { |
| 226 | /* not enough space; let JNI throw the exception */ |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 227 | LOGW("Stack overflow on PushLocal " |
| 228 | "(req=%d top=%p cur=%p size=%d '%s')\n", |
| 229 | stackReq, self->interpStackStart, self->curFrame, |
| 230 | self->interpStackSize, method->name); |
| Andy McFadden | 6ed1a0f | 2009-09-10 15:34:19 -0700 | [diff] [blame] | 231 | dvmHandleStackOverflow(self, method); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 232 | assert(dvmCheckException(self)); |
| 233 | return false; |
| 234 | } |
| 235 | |
| 236 | /* |
| 237 | * Shift the stack pointer down, leaving space for just the stack save |
| 238 | * area for the break frame, then shift down farther for the full frame. |
| 239 | */ |
| 240 | stackPtr -= sizeof(StackSaveArea); |
| 241 | saveBlock = (StackSaveArea*) stackPtr; |
| 242 | |
| 243 | #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) |
| 244 | /* debug -- memset the new stack */ |
| 245 | memset(stackPtr, 0xaf, stackReq); |
| 246 | #endif |
| 247 | #ifdef EASY_GDB |
| 248 | saveBlock->prevSave = FP_FROM_SAVEAREA(self->curFrame); |
| 249 | #endif |
| 250 | |
| 251 | saveBlock->prevFrame = self->curFrame; |
| 252 | saveBlock->savedPc = NULL; // not required |
| Andy McFadden | d5ab726 | 2009-08-25 07:19:34 -0700 | [diff] [blame] | 253 | #ifdef USE_INDIRECT_REF |
| 254 | saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all; |
| 255 | #else |
| 256 | saveBlock->xtra.localRefCookie = self->jniLocalRefTable.nextEntry; |
| 257 | #endif |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 258 | saveBlock->method = method; |
| 259 | |
| 260 | LOGVV("PUSH JNI local frame: old=%p new=%p (size=%d)\n", |
| 261 | self->curFrame, FP_FROM_SAVEAREA(saveBlock), |
| 262 | (u1*)self->curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); |
| 263 | |
| 264 | self->curFrame = FP_FROM_SAVEAREA(saveBlock); |
| 265 | |
| 266 | return true; |
| 267 | } |
| 268 | |
| 269 | /* |
| 270 | * Pop one frame pushed on by JNI PushLocalFrame. |
| 271 | * |
| 272 | * If we've gone too far, the previous frame is either a break frame or |
| 273 | * an interpreted frame. Either way, the method pointer won't match. |
| 274 | */ |
| 275 | bool dvmPopLocalFrame(Thread* self) |
| 276 | { |
| 277 | StackSaveArea* saveBlock = SAVEAREA_FROM_FP(self->curFrame); |
| 278 | |
| 279 | assert(!dvmIsBreakFrame(self->curFrame)); |
| 280 | if (saveBlock->method != SAVEAREA_FROM_FP(saveBlock->prevFrame)->method) { |
| 281 | /* |
| 282 | * The previous frame doesn't have the same method pointer -- we've |
| 283 | * been asked to pop too much. |
| 284 | */ |
| 285 | assert(dvmIsBreakFrame(saveBlock->prevFrame) || |
| 286 | !dvmIsNativeMethod( |
| 287 | SAVEAREA_FROM_FP(saveBlock->prevFrame)->method)); |
| 288 | return false; |
| 289 | } |
| 290 | |
| 291 | LOGVV("POP JNI local frame: removing %s, now %s\n", |
| 292 | saveBlock->method->name, |
| 293 | SAVEAREA_FROM_FP(saveBlock->prevFrame)->method->name); |
| 294 | dvmPopJniLocals(self, saveBlock); |
| 295 | self->curFrame = saveBlock->prevFrame; |
| 296 | |
| 297 | return true; |
| 298 | } |
| 299 | |
| 300 | /* |
| 301 | * Pop a frame we added. There should be one method frame and one break |
| 302 | * frame. |
| 303 | * |
| 304 | * If JNI Push/PopLocalFrame calls were mismatched, we might end up |
| 305 | * popping multiple method frames before we find the break. |
| 306 | * |
| 307 | * Returns "false" if there was no frame to pop. |
| 308 | */ |
| 309 | static bool dvmPopFrame(Thread* self) |
| 310 | { |
| 311 | StackSaveArea* saveBlock; |
| 312 | |
| 313 | if (self->curFrame == NULL) |
| 314 | return false; |
| 315 | |
| 316 | saveBlock = SAVEAREA_FROM_FP(self->curFrame); |
| 317 | assert(!dvmIsBreakFrame(self->curFrame)); |
| 318 | |
| 319 | /* |
| 320 | * Remove everything up to the break frame. If this was a call into |
| 321 | * native code, pop the JNI local references table. |
| 322 | */ |
| 323 | while (saveBlock->prevFrame != NULL && saveBlock->method != NULL) { |
| 324 | /* probably a native->native JNI call */ |
| 325 | |
| 326 | if (dvmIsNativeMethod(saveBlock->method)) { |
| 327 | LOGVV("Popping JNI stack frame for %s.%s%s\n", |
| 328 | saveBlock->method->clazz->descriptor, |
| 329 | saveBlock->method->name, |
| 330 | (SAVEAREA_FROM_FP(saveBlock->prevFrame)->method == NULL) ? |
| 331 | "" : " (JNI local)"); |
| Andy McFadden | d5ab726 | 2009-08-25 07:19:34 -0700 | [diff] [blame] | 332 | assert(saveBlock->xtra.localRefCookie != 0); |
| 333 | //assert(saveBlock->xtra.localRefCookie >= self->jniLocalRefTable.table && |
| 334 | // saveBlock->xtra.localRefCookie <=self->jniLocalRefTable.nextEntry); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 335 | |
| 336 | dvmPopJniLocals(self, saveBlock); |
| 337 | } |
| 338 | |
| 339 | saveBlock = SAVEAREA_FROM_FP(saveBlock->prevFrame); |
| 340 | } |
| 341 | if (saveBlock->method != NULL) { |
| 342 | LOGE("PopFrame missed the break\n"); |
| 343 | assert(false); |
| 344 | dvmAbort(); // stack trashed -- nowhere to go in this thread |
| 345 | } |
| 346 | |
| 347 | LOGVV("POP frame: cur=%p new=%p\n", |
| 348 | self->curFrame, saveBlock->prevFrame); |
| 349 | |
| 350 | self->curFrame = saveBlock->prevFrame; |
| 351 | return true; |
| 352 | } |
| 353 | |
| 354 | /* |
| 355 | * Common code for dvmCallMethodV/A and dvmInvokeMethod. |
| 356 | * |
| 357 | * Pushes a call frame on, advancing self->curFrame. |
| 358 | */ |
| 359 | static ClassObject* callPrep(Thread* self, const Method* method, Object* obj, |
| 360 | bool checkAccess) |
| 361 | { |
| 362 | ClassObject* clazz; |
| 363 | |
| 364 | #ifndef NDEBUG |
| 365 | if (self->status != THREAD_RUNNING) { |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 366 | LOGW("threadid=%d: status=%d on call to %s.%s -\n", |
| 367 | self->threadId, self->status, |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 368 | method->clazz->descriptor, method->name); |
| 369 | } |
| 370 | #endif |
| 371 | |
| 372 | assert(self != NULL); |
| 373 | assert(method != NULL); |
| 374 | |
| 375 | if (obj != NULL) |
| 376 | clazz = obj->clazz; |
| 377 | else |
| 378 | clazz = method->clazz; |
| 379 | |
| 380 | IF_LOGVV() { |
| 381 | char* desc = dexProtoCopyMethodDescriptor(&method->prototype); |
| 382 | LOGVV("thread=%d native code calling %s.%s %s\n", self->threadId, |
| 383 | clazz->descriptor, method->name, desc); |
| 384 | free(desc); |
| 385 | } |
| 386 | |
| 387 | if (checkAccess) { |
| 388 | /* needed for java.lang.reflect.Method.invoke */ |
| 389 | if (!dvmCheckMethodAccess(dvmGetCaller2Class(self->curFrame), |
| 390 | method)) |
| 391 | { |
| 392 | /* note this throws IAException, not IAError */ |
| 393 | dvmThrowException("Ljava/lang/IllegalAccessException;", |
| 394 | "access to method denied"); |
| 395 | return NULL; |
| 396 | } |
| 397 | } |
| 398 | |
| 399 | /* |
| 400 | * Push a call frame on. If there isn't enough room for ins, locals, |
| 401 | * outs, and the saved state, it will throw an exception. |
| 402 | * |
| 403 | * This updates self->curFrame. |
| 404 | */ |
| 405 | if (dvmIsNativeMethod(method)) { |
| 406 | /* native code calling native code the hard way */ |
| 407 | if (!dvmPushJNIFrame(self, method)) { |
| 408 | assert(dvmCheckException(self)); |
| 409 | return NULL; |
| 410 | } |
| 411 | } else { |
| 412 | /* native code calling interpreted code */ |
| 413 | if (!dvmPushInterpFrame(self, method)) { |
| 414 | assert(dvmCheckException(self)); |
| 415 | return NULL; |
| 416 | } |
| 417 | } |
| 418 | |
| 419 | return clazz; |
| 420 | } |
| 421 | |
| 422 | /* |
| 423 | * Issue a method call. |
| 424 | * |
| 425 | * Pass in NULL for "obj" on calls to static methods. |
| 426 | * |
| 427 | * (Note this can't be inlined because it takes a variable number of args.) |
| 428 | */ |
| 429 | void dvmCallMethod(Thread* self, const Method* method, Object* obj, |
| 430 | JValue* pResult, ...) |
| 431 | { |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 432 | va_list args; |
| 433 | va_start(args, pResult); |
| Andy McFadden | d5ab726 | 2009-08-25 07:19:34 -0700 | [diff] [blame] | 434 | dvmCallMethodV(self, method, obj, false, pResult, args); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 435 | va_end(args); |
| 436 | } |
| 437 | |
| 438 | /* |
| 439 | * Issue a method call with a variable number of arguments. We process |
| 440 | * the contents of "args" by scanning the method signature. |
| 441 | * |
| 442 | * Pass in NULL for "obj" on calls to static methods. |
| 443 | * |
| 444 | * We don't need to take the class as an argument because, in Dalvik, |
| 445 | * we don't need to worry about static synchronized methods. |
| 446 | */ |
| 447 | void dvmCallMethodV(Thread* self, const Method* method, Object* obj, |
| Andy McFadden | d5ab726 | 2009-08-25 07:19:34 -0700 | [diff] [blame] | 448 | bool fromJni, JValue* pResult, va_list args) |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 449 | { |
| 450 | const char* desc = &(method->shorty[1]); // [0] is the return type. |
| 451 | int verifyCount = 0; |
| 452 | ClassObject* clazz; |
| 453 | u4* ins; |
| 454 | |
| 455 | clazz = callPrep(self, method, obj, false); |
| 456 | if (clazz == NULL) |
| 457 | return; |
| 458 | |
| 459 | /* "ins" for new frame start at frame pointer plus locals */ |
| 460 | ins = ((u4*)self->curFrame) + (method->registersSize - method->insSize); |
| 461 | |
| 462 | //LOGD(" FP is %p, INs live at >= %p\n", self->curFrame, ins); |
| 463 | |
| 464 | /* put "this" pointer into in0 if appropriate */ |
| 465 | if (!dvmIsStaticMethod(method)) { |
| 466 | #ifdef WITH_EXTRA_OBJECT_VALIDATION |
| 467 | assert(obj != NULL && dvmIsValidObject(obj)); |
| 468 | #endif |
| 469 | *ins++ = (u4) obj; |
| 470 | verifyCount++; |
| 471 | } |
| 472 | |
| Andy McFadden | 0083d37 | 2009-08-21 14:44:04 -0700 | [diff] [blame] | 473 | JNIEnv* env = self->jniEnv; |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 474 | while (*desc != '\0') { |
| 475 | switch (*(desc++)) { |
| 476 | case 'D': case 'J': { |
| 477 | u8 val = va_arg(args, u8); |
| 478 | memcpy(ins, &val, 8); // EABI prevents direct store |
| 479 | ins += 2; |
| 480 | verifyCount += 2; |
| 481 | break; |
| 482 | } |
| 483 | case 'F': { |
| 484 | /* floats were normalized to doubles; convert back */ |
| 485 | float f = (float) va_arg(args, double); |
| 486 | *ins++ = dvmFloatToU4(f); |
| 487 | verifyCount++; |
| 488 | break; |
| 489 | } |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 490 | case 'L': { /* 'shorty' descr uses L for all refs, incl array */ |
| Andy McFadden | 0083d37 | 2009-08-21 14:44:04 -0700 | [diff] [blame] | 491 | void* argObj = va_arg(args, void*); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 492 | assert(obj == NULL || dvmIsValidObject(obj)); |
| Andy McFadden | d5ab726 | 2009-08-25 07:19:34 -0700 | [diff] [blame] | 493 | if (fromJni) |
| 494 | *ins++ = (u4) dvmDecodeIndirectRef(env, argObj); |
| 495 | else |
| 496 | *ins++ = (u4) argObj; |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 497 | verifyCount++; |
| 498 | break; |
| 499 | } |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 500 | default: { |
| Andy McFadden | 0083d37 | 2009-08-21 14:44:04 -0700 | [diff] [blame] | 501 | /* Z B C S I -- all passed as 32-bit integers */ |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 502 | *ins++ = va_arg(args, u4); |
| 503 | verifyCount++; |
| 504 | break; |
| 505 | } |
| 506 | } |
| 507 | } |
| 508 | |
| 509 | #ifndef NDEBUG |
| 510 | if (verifyCount != method->insSize) { |
| 511 | LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount, |
| 512 | method->insSize, clazz->descriptor, method->name); |
| 513 | assert(false); |
| 514 | goto bail; |
| 515 | } |
| 516 | #endif |
| 517 | |
| 518 | //dvmDumpThreadStack(dvmThreadSelf()); |
| 519 | |
| 520 | if (dvmIsNativeMethod(method)) { |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 521 | TRACE_METHOD_ENTER(self, method); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 522 | /* |
| 523 | * Because we leave no space for local variables, "curFrame" points |
| 524 | * directly at the method arguments. |
| 525 | */ |
| 526 | (*method->nativeFunc)(self->curFrame, pResult, method, self); |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 527 | TRACE_METHOD_EXIT(self, method); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 528 | } else { |
| 529 | dvmInterpret(self, method, pResult); |
| 530 | } |
| 531 | |
| Carl Shapiro | e3c01da | 2010-05-20 22:54:18 -0700 | [diff] [blame] | 532 | #ifndef NDEBUG |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 533 | bail: |
| Carl Shapiro | e3c01da | 2010-05-20 22:54:18 -0700 | [diff] [blame] | 534 | #endif |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 535 | dvmPopFrame(self); |
| 536 | } |
| 537 | |
| 538 | /* |
| 539 | * Issue a method call with arguments provided in an array. We process |
| 540 | * the contents of "args" by scanning the method signature. |
| 541 | * |
| 542 | * The values were likely placed into an uninitialized jvalue array using |
| 543 | * the field specifiers, which means that sub-32-bit fields (e.g. short, |
| 544 | * boolean) may not have 32 or 64 bits of valid data. This is different |
| 545 | * from the varargs invocation where the C compiler does a widening |
| 546 | * conversion when calling a function. As a result, we have to be a |
| 547 | * little more precise when pulling stuff out. |
| Andy McFadden | 8e5c784 | 2009-07-23 17:47:18 -0700 | [diff] [blame] | 548 | * |
| 549 | * "args" may be NULL if the method has no arguments. |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 550 | */ |
| 551 | void dvmCallMethodA(Thread* self, const Method* method, Object* obj, |
| Andy McFadden | d5ab726 | 2009-08-25 07:19:34 -0700 | [diff] [blame] | 552 | bool fromJni, JValue* pResult, const jvalue* args) |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 553 | { |
| 554 | const char* desc = &(method->shorty[1]); // [0] is the return type. |
| 555 | int verifyCount = 0; |
| 556 | ClassObject* clazz; |
| 557 | u4* ins; |
| 558 | |
| 559 | clazz = callPrep(self, method, obj, false); |
| 560 | if (clazz == NULL) |
| 561 | return; |
| 562 | |
| 563 | /* "ins" for new frame start at frame pointer plus locals */ |
| 564 | ins = ((u4*)self->curFrame) + (method->registersSize - method->insSize); |
| 565 | |
| 566 | /* put "this" pointer into in0 if appropriate */ |
| 567 | if (!dvmIsStaticMethod(method)) { |
| 568 | assert(obj != NULL); |
| Andy McFadden | 0083d37 | 2009-08-21 14:44:04 -0700 | [diff] [blame] | 569 | *ins++ = (u4) obj; /* obj is a "real" ref */ |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 570 | verifyCount++; |
| 571 | } |
| 572 | |
| Andy McFadden | 0083d37 | 2009-08-21 14:44:04 -0700 | [diff] [blame] | 573 | JNIEnv* env = self->jniEnv; |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 574 | while (*desc != '\0') { |
| Andy McFadden | 0083d37 | 2009-08-21 14:44:04 -0700 | [diff] [blame] | 575 | switch (*desc++) { |
| 576 | case 'D': /* 64-bit quantity; have to use */ |
| 577 | case 'J': /* memcpy() in case of mis-alignment */ |
| 578 | memcpy(ins, &args->j, 8); |
| 579 | ins += 2; |
| 580 | verifyCount++; /* this needs an extra push */ |
| 581 | break; |
| 582 | case 'L': /* includes array refs */ |
| Andy McFadden | d5ab726 | 2009-08-25 07:19:34 -0700 | [diff] [blame] | 583 | if (fromJni) |
| 584 | *ins++ = (u4) dvmDecodeIndirectRef(env, args->l); |
| 585 | else |
| 586 | *ins++ = (u4) args->l; |
| Andy McFadden | 0083d37 | 2009-08-21 14:44:04 -0700 | [diff] [blame] | 587 | break; |
| 588 | case 'F': |
| 589 | case 'I': |
| 590 | *ins++ = args->i; /* full 32 bits */ |
| 591 | break; |
| 592 | case 'S': |
| 593 | *ins++ = args->s; /* 16 bits, sign-extended */ |
| 594 | break; |
| 595 | case 'C': |
| 596 | *ins++ = args->c; /* 16 bits, unsigned */ |
| 597 | break; |
| 598 | case 'B': |
| 599 | *ins++ = args->b; /* 8 bits, sign-extended */ |
| 600 | break; |
| 601 | case 'Z': |
| 602 | *ins++ = args->z; /* 8 bits, zero or non-zero */ |
| 603 | break; |
| 604 | default: |
| 605 | LOGE("Invalid char %c in short signature of %s.%s\n", |
| 606 | *(desc-1), clazz->descriptor, method->name); |
| 607 | assert(false); |
| 608 | goto bail; |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 609 | } |
| Andy McFadden | 0083d37 | 2009-08-21 14:44:04 -0700 | [diff] [blame] | 610 | |
| 611 | verifyCount++; |
| 612 | args++; |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 613 | } |
| 614 | |
| 615 | #ifndef NDEBUG |
| 616 | if (verifyCount != method->insSize) { |
| 617 | LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount, |
| 618 | method->insSize, clazz->descriptor, method->name); |
| 619 | assert(false); |
| 620 | goto bail; |
| 621 | } |
| 622 | #endif |
| 623 | |
| 624 | if (dvmIsNativeMethod(method)) { |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 625 | TRACE_METHOD_ENTER(self, method); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 626 | /* |
| 627 | * Because we leave no space for local variables, "curFrame" points |
| 628 | * directly at the method arguments. |
| 629 | */ |
| 630 | (*method->nativeFunc)(self->curFrame, pResult, method, self); |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 631 | TRACE_METHOD_EXIT(self, method); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 632 | } else { |
| 633 | dvmInterpret(self, method, pResult); |
| 634 | } |
| 635 | |
| 636 | bail: |
| 637 | dvmPopFrame(self); |
| 638 | } |
| 639 | |
| Elliott Hughes | 07946cf | 2010-12-03 17:59:32 -0800 | [diff] [blame^] | 640 | static void throwArgumentTypeMismatch(int argIndex, ClassObject* expected, |
| 641 | DataObject* arg) |
| 642 | { |
| 643 | char* expectedClassName = dvmHumanReadableDescriptor(expected->descriptor); |
| 644 | char* actualClassName = (arg != NULL) |
| 645 | ? dvmHumanReadableDescriptor(arg->obj.clazz->descriptor) |
| 646 | : strdup("null"); |
| 647 | dvmThrowExceptionFmt("Ljava/lang/IllegalArgumentException;", |
| 648 | "argument %d should have type %s, got %s", |
| 649 | argIndex + 1, expectedClassName, actualClassName); |
| 650 | free(expectedClassName); |
| 651 | free(actualClassName); |
| 652 | } |
| 653 | |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 654 | /* |
| 655 | * Invoke a method, using the specified arguments and return type, through |
| 656 | * one of the reflection interfaces. Could be a virtual or direct method |
| 657 | * (including constructors). Used for reflection. |
| 658 | * |
| 659 | * Deals with boxing/unboxing primitives and performs widening conversions. |
| 660 | * |
| 661 | * "invokeObj" will be null for a static method. |
| 662 | * |
| 663 | * If the invocation returns with an exception raised, we have to wrap it. |
| 664 | */ |
| 665 | Object* dvmInvokeMethod(Object* obj, const Method* method, |
| 666 | ArrayObject* argList, ArrayObject* params, ClassObject* returnType, |
| 667 | bool noAccessCheck) |
| 668 | { |
| 669 | ClassObject* clazz; |
| 670 | Object* retObj = NULL; |
| 671 | Thread* self = dvmThreadSelf(); |
| 672 | s4* ins; |
| 673 | int verifyCount, argListLength; |
| 674 | JValue retval; |
| Andy McFadden | be420e7 | 2010-10-18 13:28:31 -0700 | [diff] [blame] | 675 | bool needPop = false; |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 676 | |
| 677 | /* verify arg count */ |
| 678 | if (argList != NULL) |
| 679 | argListLength = argList->length; |
| 680 | else |
| 681 | argListLength = 0; |
| 682 | if (argListLength != (int) params->length) { |
| Elliott Hughes | 07946cf | 2010-12-03 17:59:32 -0800 | [diff] [blame^] | 683 | dvmThrowExceptionFmt("Ljava/lang/IllegalArgumentException;", |
| 684 | "wrong number of arguments; expected %d, got %d", |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 685 | params->length, argListLength); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 686 | return NULL; |
| 687 | } |
| 688 | |
| 689 | clazz = callPrep(self, method, obj, !noAccessCheck); |
| 690 | if (clazz == NULL) |
| 691 | return NULL; |
| Andy McFadden | be420e7 | 2010-10-18 13:28:31 -0700 | [diff] [blame] | 692 | needPop = true; |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 693 | |
| 694 | /* "ins" for new frame start at frame pointer plus locals */ |
| 695 | ins = ((s4*)self->curFrame) + (method->registersSize - method->insSize); |
| 696 | verifyCount = 0; |
| 697 | |
| 698 | //LOGD(" FP is %p, INs live at >= %p\n", self->curFrame, ins); |
| 699 | |
| 700 | /* put "this" pointer into in0 if appropriate */ |
| 701 | if (!dvmIsStaticMethod(method)) { |
| 702 | assert(obj != NULL); |
| 703 | *ins++ = (s4) obj; |
| 704 | verifyCount++; |
| 705 | } |
| 706 | |
| 707 | /* |
| 708 | * Copy the args onto the stack. Primitive types are converted when |
| 709 | * necessary, and object types are verified. |
| 710 | */ |
| 711 | DataObject** args; |
| 712 | ClassObject** types; |
| 713 | int i; |
| 714 | |
| 715 | args = (DataObject**) argList->contents; |
| 716 | types = (ClassObject**) params->contents; |
| 717 | for (i = 0; i < argListLength; i++) { |
| 718 | int width; |
| 719 | |
| 720 | width = dvmConvertArgument(*args++, *types++, ins); |
| 721 | if (width < 0) { |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 722 | dvmPopFrame(self); // throw wants to pull PC out of stack |
| Andy McFadden | be420e7 | 2010-10-18 13:28:31 -0700 | [diff] [blame] | 723 | needPop = false; |
| Elliott Hughes | 07946cf | 2010-12-03 17:59:32 -0800 | [diff] [blame^] | 724 | throwArgumentTypeMismatch(i, *(types-1), *(args-1)); |
| Andy McFadden | be420e7 | 2010-10-18 13:28:31 -0700 | [diff] [blame] | 725 | goto bail; |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 726 | } |
| 727 | |
| 728 | ins += width; |
| 729 | verifyCount += width; |
| 730 | } |
| 731 | |
| Andy McFadden | c4ae06f | 2010-11-04 12:37:51 -0700 | [diff] [blame] | 732 | #ifndef NDEBUG |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 733 | if (verifyCount != method->insSize) { |
| 734 | LOGE("Got vfycount=%d insSize=%d for %s.%s\n", verifyCount, |
| 735 | method->insSize, clazz->descriptor, method->name); |
| 736 | assert(false); |
| 737 | goto bail; |
| 738 | } |
| Andy McFadden | c4ae06f | 2010-11-04 12:37:51 -0700 | [diff] [blame] | 739 | #endif |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 740 | |
| 741 | if (dvmIsNativeMethod(method)) { |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 742 | TRACE_METHOD_ENTER(self, method); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 743 | /* |
| 744 | * Because we leave no space for local variables, "curFrame" points |
| 745 | * directly at the method arguments. |
| 746 | */ |
| 747 | (*method->nativeFunc)(self->curFrame, &retval, method, self); |
| The Android Open Source Project | 9940988 | 2009-03-18 22:20:24 -0700 | [diff] [blame] | 748 | TRACE_METHOD_EXIT(self, method); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 749 | } else { |
| 750 | dvmInterpret(self, method, &retval); |
| 751 | } |
| 752 | |
| 753 | /* |
| Andy McFadden | be420e7 | 2010-10-18 13:28:31 -0700 | [diff] [blame] | 754 | * Pop the frame immediately. The "wrap" calls below can cause |
| 755 | * allocations, and we don't want the GC to walk the now-dead frame. |
| 756 | */ |
| 757 | dvmPopFrame(self); |
| 758 | needPop = false; |
| 759 | |
| 760 | /* |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 761 | * If an exception is raised, wrap and replace. This is necessary |
| 762 | * because the invoked method could have thrown a checked exception |
| 763 | * that the caller wasn't prepared for. |
| 764 | * |
| 765 | * We might be able to do this up in the interpreted code, but that will |
| 766 | * leave us with a shortened stack trace in the top-level exception. |
| 767 | */ |
| 768 | if (dvmCheckException(self)) { |
| 769 | dvmWrapException("Ljava/lang/reflect/InvocationTargetException;"); |
| 770 | } else { |
| 771 | /* |
| 772 | * If this isn't a void method or constructor, convert the return type |
| 773 | * to an appropriate object. |
| 774 | * |
| 775 | * We don't do this when an exception is raised because the value |
| 776 | * in "retval" is undefined. |
| 777 | */ |
| 778 | if (returnType != NULL) { |
| Andy McFadden | c4ae06f | 2010-11-04 12:37:51 -0700 | [diff] [blame] | 779 | retObj = (Object*)dvmBoxPrimitive(retval, returnType); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 780 | dvmReleaseTrackedAlloc(retObj, NULL); |
| 781 | } |
| 782 | } |
| 783 | |
| 784 | bail: |
| Andy McFadden | be420e7 | 2010-10-18 13:28:31 -0700 | [diff] [blame] | 785 | if (needPop) { |
| 786 | dvmPopFrame(self); |
| 787 | } |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 788 | return retObj; |
| 789 | } |
| 790 | |
| 791 | typedef struct LineNumFromPcContext { |
| 792 | u4 address; |
| 793 | u4 lineNum; |
| 794 | } LineNumFromPcContext; |
| 795 | |
| 796 | static int lineNumForPcCb(void *cnxt, u4 address, u4 lineNum) |
| 797 | { |
| 798 | LineNumFromPcContext *pContext = (LineNumFromPcContext *)cnxt; |
| 799 | |
| Carl Shapiro | de75089 | 2010-06-08 16:37:12 -0700 | [diff] [blame] | 800 | // We know that this callback will be called in |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 801 | // ascending address order, so keep going until we find |
| 802 | // a match or we've just gone past it. |
| 803 | |
| 804 | if (address > pContext->address) { |
| 805 | // The line number from the previous positions callback |
| 806 | // wil be the final result. |
| 807 | return 1; |
| 808 | } |
| 809 | |
| 810 | pContext->lineNum = lineNum; |
| 811 | |
| 812 | return (address == pContext->address) ? 1 : 0; |
| 813 | } |
| 814 | |
| 815 | /* |
| 816 | * Determine the source file line number based on the program counter. |
| 817 | * "pc" is an offset, in 16-bit units, from the start of the method's code. |
| 818 | * |
| 819 | * Returns -1 if no match was found (possibly because the source files were |
| 820 | * compiled without "-g", so no line number information is present). |
| 821 | * Returns -2 for native methods (as expected in exception traces). |
| 822 | */ |
| 823 | int dvmLineNumFromPC(const Method* method, u4 relPc) |
| 824 | { |
| 825 | const DexCode* pDexCode = dvmGetMethodCode(method); |
| 826 | |
| 827 | if (pDexCode == NULL) { |
| 828 | if (dvmIsNativeMethod(method) && !dvmIsAbstractMethod(method)) |
| 829 | return -2; |
| 830 | return -1; /* can happen for abstract method stub */ |
| 831 | } |
| 832 | |
| 833 | LineNumFromPcContext context; |
| 834 | memset(&context, 0, sizeof(context)); |
| 835 | context.address = relPc; |
| 836 | // A method with no line number info should return -1 |
| 837 | context.lineNum = -1; |
| 838 | |
| 839 | dexDecodeDebugInfo(method->clazz->pDvmDex->pDexFile, pDexCode, |
| 840 | method->clazz->descriptor, |
| 841 | method->prototype.protoIdx, |
| 842 | method->accessFlags, |
| 843 | lineNumForPcCb, NULL, &context); |
| Carl Shapiro | de75089 | 2010-06-08 16:37:12 -0700 | [diff] [blame] | 844 | |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 845 | return context.lineNum; |
| 846 | } |
| 847 | |
| 848 | /* |
| 849 | * Compute the frame depth. |
| 850 | * |
| 851 | * Excludes "break" frames. |
| 852 | */ |
| 853 | int dvmComputeExactFrameDepth(const void* fp) |
| 854 | { |
| 855 | int count = 0; |
| 856 | |
| 857 | for ( ; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) { |
| 858 | if (!dvmIsBreakFrame(fp)) |
| 859 | count++; |
| 860 | } |
| 861 | |
| 862 | return count; |
| 863 | } |
| 864 | |
| 865 | /* |
| 866 | * Compute the "vague" frame depth, which is just a pointer subtraction. |
| 867 | * The result is NOT an overly generous assessment of the number of |
| 868 | * frames; the only meaningful use is to compare against the result of |
| 869 | * an earlier invocation. |
| 870 | * |
| 871 | * Useful for implementing single-step debugger modes, which may need to |
| 872 | * call this for every instruction. |
| 873 | */ |
| 874 | int dvmComputeVagueFrameDepth(Thread* thread, const void* fp) |
| 875 | { |
| 876 | const u1* interpStackStart = thread->interpStackStart; |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 877 | |
| Carl Shapiro | e3c01da | 2010-05-20 22:54:18 -0700 | [diff] [blame] | 878 | assert((u1*) fp >= interpStackStart - thread->interpStackSize); |
| 879 | assert((u1*) fp < interpStackStart); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 880 | return interpStackStart - (u1*) fp; |
| 881 | } |
| 882 | |
| 883 | /* |
| 884 | * Get the calling frame. Pass in the current fp. |
| 885 | * |
| 886 | * Skip "break" frames and reflection invoke frames. |
| 887 | */ |
| 888 | void* dvmGetCallerFP(const void* curFrame) |
| 889 | { |
| 890 | void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame; |
| 891 | StackSaveArea* saveArea; |
| 892 | |
| 893 | retry: |
| 894 | if (dvmIsBreakFrame(caller)) { |
| 895 | /* pop up one more */ |
| 896 | caller = SAVEAREA_FROM_FP(caller)->prevFrame; |
| 897 | if (caller == NULL) |
| 898 | return NULL; /* hit the top */ |
| 899 | |
| 900 | /* |
| 901 | * If we got here by java.lang.reflect.Method.invoke(), we don't |
| 902 | * want to return Method's class loader. Shift up one and try |
| 903 | * again. |
| 904 | */ |
| 905 | saveArea = SAVEAREA_FROM_FP(caller); |
| 906 | if (dvmIsReflectionMethod(saveArea->method)) { |
| 907 | caller = saveArea->prevFrame; |
| 908 | assert(caller != NULL); |
| 909 | goto retry; |
| 910 | } |
| 911 | } |
| 912 | |
| 913 | return caller; |
| 914 | } |
| 915 | |
| 916 | /* |
| 917 | * Get the caller's class. Pass in the current fp. |
| 918 | * |
| 919 | * This is used by e.g. java.lang.Class. |
| 920 | */ |
| 921 | ClassObject* dvmGetCallerClass(const void* curFrame) |
| 922 | { |
| 923 | void* caller; |
| 924 | |
| 925 | caller = dvmGetCallerFP(curFrame); |
| 926 | if (caller == NULL) |
| 927 | return NULL; |
| 928 | |
| 929 | return SAVEAREA_FROM_FP(caller)->method->clazz; |
| 930 | } |
| 931 | |
| 932 | /* |
| 933 | * Get the caller's caller's class. Pass in the current fp. |
| 934 | * |
| 935 | * This is used by e.g. java.lang.Class, which wants to know about the |
| 936 | * class loader of the method that called it. |
| 937 | */ |
| 938 | ClassObject* dvmGetCaller2Class(const void* curFrame) |
| 939 | { |
| 940 | void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame; |
| 941 | void* callerCaller; |
| 942 | |
| 943 | /* at the top? */ |
| 944 | if (dvmIsBreakFrame(caller) && SAVEAREA_FROM_FP(caller)->prevFrame == NULL) |
| 945 | return NULL; |
| 946 | |
| 947 | /* go one more */ |
| 948 | callerCaller = dvmGetCallerFP(caller); |
| 949 | if (callerCaller == NULL) |
| 950 | return NULL; |
| 951 | |
| 952 | return SAVEAREA_FROM_FP(callerCaller)->method->clazz; |
| 953 | } |
| 954 | |
| 955 | /* |
| 956 | * Get the caller's caller's caller's class. Pass in the current fp. |
| 957 | * |
| 958 | * This is used by e.g. java.lang.Class, which wants to know about the |
| 959 | * class loader of the method that called it. |
| 960 | */ |
| 961 | ClassObject* dvmGetCaller3Class(const void* curFrame) |
| 962 | { |
| 963 | void* caller = SAVEAREA_FROM_FP(curFrame)->prevFrame; |
| 964 | int i; |
| 965 | |
| 966 | /* at the top? */ |
| 967 | if (dvmIsBreakFrame(caller) && SAVEAREA_FROM_FP(caller)->prevFrame == NULL) |
| 968 | return NULL; |
| 969 | |
| 970 | /* Walk up two frames if possible. */ |
| 971 | for (i = 0; i < 2; i++) { |
| 972 | caller = dvmGetCallerFP(caller); |
| 973 | if (caller == NULL) |
| 974 | return NULL; |
| 975 | } |
| Carl Shapiro | de75089 | 2010-06-08 16:37:12 -0700 | [diff] [blame] | 976 | |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 977 | return SAVEAREA_FROM_FP(caller)->method->clazz; |
| 978 | } |
| 979 | |
| 980 | /* |
| 981 | * Create a flat array of methods that comprise the current interpreter |
| 982 | * stack trace. Pass in the current frame ptr. |
| 983 | * |
| 984 | * Allocates a new array and fills it with method pointers. Break frames |
| 985 | * are skipped, but reflection invocations are not. The caller must free |
| 986 | * "*pArray". |
| 987 | * |
| 988 | * The current frame will be in element 0. |
| 989 | * |
| 990 | * Returns "true" on success, "false" on failure (e.g. malloc failed). |
| 991 | */ |
| 992 | bool dvmCreateStackTraceArray(const void* fp, const Method*** pArray, |
| 993 | int* pLength) |
| 994 | { |
| 995 | const Method** array; |
| 996 | int idx, depth; |
| 997 | |
| 998 | depth = dvmComputeExactFrameDepth(fp); |
| 999 | array = (const Method**) malloc(depth * sizeof(Method*)); |
| 1000 | if (array == NULL) |
| 1001 | return false; |
| 1002 | |
| 1003 | for (idx = 0; fp != NULL; fp = SAVEAREA_FROM_FP(fp)->prevFrame) { |
| 1004 | if (!dvmIsBreakFrame(fp)) |
| 1005 | array[idx++] = SAVEAREA_FROM_FP(fp)->method; |
| 1006 | } |
| 1007 | assert(idx == depth); |
| 1008 | |
| 1009 | *pArray = array; |
| 1010 | *pLength = depth; |
| 1011 | return true; |
| 1012 | } |
| 1013 | |
| 1014 | /* |
| 1015 | * Open up the reserved area and throw an exception. The reserved area |
| 1016 | * should only be needed to create and initialize the exception itself. |
| 1017 | * |
| 1018 | * If we already opened it and we're continuing to overflow, abort the VM. |
| 1019 | * |
| 1020 | * We have to leave the "reserved" area open until the "catch" handler has |
| 1021 | * finished doing its processing. This is because the catch handler may |
| 1022 | * need to resolve classes, which requires calling into the class loader if |
| 1023 | * the classes aren't already in the "initiating loader" list. |
| 1024 | */ |
| Andy McFadden | 6ed1a0f | 2009-09-10 15:34:19 -0700 | [diff] [blame] | 1025 | void dvmHandleStackOverflow(Thread* self, const Method* method) |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1026 | { |
| 1027 | /* |
| 1028 | * Can we make the reserved area available? |
| 1029 | */ |
| 1030 | if (self->stackOverflowed) { |
| 1031 | /* |
| 1032 | * Already did, nothing to do but bail. |
| 1033 | */ |
| 1034 | LOGE("DalvikVM: double-overflow of stack in threadid=%d; aborting\n", |
| 1035 | self->threadId); |
| 1036 | dvmDumpThread(self, false); |
| 1037 | dvmAbort(); |
| 1038 | } |
| 1039 | |
| 1040 | /* open it up to the full range */ |
| Andy McFadden | 6ed1a0f | 2009-09-10 15:34:19 -0700 | [diff] [blame] | 1041 | LOGI("threadid=%d: stack overflow on call to %s.%s:%s\n", |
| 1042 | self->threadId, |
| 1043 | method->clazz->descriptor, method->name, method->shorty); |
| 1044 | StackSaveArea* saveArea = SAVEAREA_FROM_FP(self->curFrame); |
| 1045 | LOGI(" method requires %d+%d+%d=%d bytes, fp is %p (%d left)\n", |
| 1046 | method->registersSize * 4, sizeof(StackSaveArea), method->outsSize * 4, |
| 1047 | (method->registersSize + method->outsSize) * 4 + sizeof(StackSaveArea), |
| 1048 | saveArea, (u1*) saveArea - self->interpStackEnd); |
| 1049 | LOGI(" expanding stack end (%p to %p)\n", self->interpStackEnd, |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1050 | self->interpStackStart - self->interpStackSize); |
| 1051 | //dvmDumpThread(self, false); |
| 1052 | self->interpStackEnd = self->interpStackStart - self->interpStackSize; |
| 1053 | self->stackOverflowed = true; |
| 1054 | |
| 1055 | /* |
| 1056 | * If we were trying to throw an exception when the stack overflowed, |
| 1057 | * we will blow up when doing the class lookup on StackOverflowError |
| 1058 | * because of the pending exception. So, we clear it and make it |
| 1059 | * the cause of the SOE. |
| 1060 | */ |
| 1061 | Object* excep = dvmGetException(self); |
| 1062 | if (excep != NULL) { |
| 1063 | LOGW("Stack overflow while throwing exception\n"); |
| 1064 | dvmClearException(self); |
| 1065 | } |
| Andy McFadden | 4fbba1f | 2010-02-03 07:21:14 -0800 | [diff] [blame] | 1066 | dvmThrowChainedExceptionByClass(gDvm.classJavaLangStackOverflowError, |
| 1067 | NULL, excep); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1068 | } |
| 1069 | |
| 1070 | /* |
| 1071 | * Reduce the available stack size. By this point we should have finished |
| 1072 | * our overflow processing. |
| 1073 | */ |
| Andy McFadden | 4fbba1f | 2010-02-03 07:21:14 -0800 | [diff] [blame] | 1074 | void dvmCleanupStackOverflow(Thread* self, const Object* exception) |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1075 | { |
| 1076 | const u1* newStackEnd; |
| 1077 | |
| 1078 | assert(self->stackOverflowed); |
| 1079 | |
| Andy McFadden | 4fbba1f | 2010-02-03 07:21:14 -0800 | [diff] [blame] | 1080 | if (exception->clazz != gDvm.classJavaLangStackOverflowError) { |
| 1081 | /* exception caused during SOE, not the SOE itself */ |
| 1082 | return; |
| 1083 | } |
| 1084 | |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1085 | newStackEnd = (self->interpStackStart - self->interpStackSize) |
| 1086 | + STACK_OVERFLOW_RESERVE; |
| 1087 | if ((u1*)self->curFrame <= newStackEnd) { |
| 1088 | LOGE("Can't shrink stack: curFrame is in reserved area (%p %p)\n", |
| 1089 | self->interpStackEnd, self->curFrame); |
| 1090 | dvmDumpThread(self, false); |
| 1091 | dvmAbort(); |
| 1092 | } |
| 1093 | |
| 1094 | self->interpStackEnd = newStackEnd; |
| 1095 | self->stackOverflowed = false; |
| 1096 | |
| 1097 | LOGI("Shrank stack (to %p, curFrame is %p)\n", self->interpStackEnd, |
| 1098 | self->curFrame); |
| 1099 | } |
| 1100 | |
| 1101 | |
| 1102 | /* |
| Andy McFadden | fd54266 | 2010-03-12 13:39:59 -0800 | [diff] [blame] | 1103 | * Extract the object that is the target of a monitor-enter instruction |
| 1104 | * in the top stack frame of "thread". |
| 1105 | * |
| 1106 | * The other thread might be alive, so this has to work carefully. |
| 1107 | * |
| Andy McFadden | d19988d | 2010-10-22 13:32:12 -0700 | [diff] [blame] | 1108 | * The thread list lock must be held. |
| Andy McFadden | fd54266 | 2010-03-12 13:39:59 -0800 | [diff] [blame] | 1109 | * |
| 1110 | * Returns "true" if we successfully recover the object. "*pOwner" will |
| 1111 | * be NULL if we can't determine the owner for some reason (e.g. race |
| 1112 | * condition on ownership transfer). |
| 1113 | */ |
| 1114 | static bool extractMonitorEnterObject(Thread* thread, Object** pLockObj, |
| 1115 | Thread** pOwner) |
| 1116 | { |
| 1117 | void* framePtr = thread->curFrame; |
| 1118 | |
| 1119 | if (framePtr == NULL || dvmIsBreakFrame(framePtr)) |
| 1120 | return false; |
| 1121 | |
| 1122 | const StackSaveArea* saveArea = SAVEAREA_FROM_FP(framePtr); |
| 1123 | const Method* method = saveArea->method; |
| 1124 | const u2* currentPc = saveArea->xtra.currentPc; |
| 1125 | |
| 1126 | /* check Method* */ |
| 1127 | if (!dvmLinearAllocContains(method, sizeof(Method))) { |
| 1128 | LOGD("ExtrMon: method %p not valid\n", method); |
| 1129 | return false; |
| 1130 | } |
| 1131 | |
| 1132 | /* check currentPc */ |
| 1133 | u4 insnsSize = dvmGetMethodInsnsSize(method); |
| 1134 | if (currentPc < method->insns || |
| 1135 | currentPc >= method->insns + insnsSize) |
| 1136 | { |
| 1137 | LOGD("ExtrMon: insns %p not valid (%p - %p)\n", |
| 1138 | currentPc, method->insns, method->insns + insnsSize); |
| 1139 | return false; |
| 1140 | } |
| 1141 | |
| 1142 | /* check the instruction */ |
| 1143 | if ((*currentPc & 0xff) != OP_MONITOR_ENTER) { |
| 1144 | LOGD("ExtrMon: insn at %p is not monitor-enter (0x%02x)\n", |
| 1145 | currentPc, *currentPc & 0xff); |
| 1146 | return false; |
| 1147 | } |
| 1148 | |
| 1149 | /* get and check the register index */ |
| 1150 | unsigned int reg = *currentPc >> 8; |
| 1151 | if (reg >= method->registersSize) { |
| 1152 | LOGD("ExtrMon: invalid register %d (max %d)\n", |
| 1153 | reg, method->registersSize); |
| 1154 | return false; |
| 1155 | } |
| 1156 | |
| 1157 | /* get and check the object in that register */ |
| 1158 | u4* fp = (u4*) framePtr; |
| 1159 | Object* obj = (Object*) fp[reg]; |
| 1160 | if (!dvmIsValidObject(obj)) { |
| 1161 | LOGD("ExtrMon: invalid object %p at %p[%d]\n", obj, fp, reg); |
| 1162 | return false; |
| 1163 | } |
| 1164 | *pLockObj = obj; |
| 1165 | |
| 1166 | /* |
| 1167 | * Try to determine the object's lock holder; it's okay if this fails. |
| 1168 | * |
| 1169 | * We're assuming the thread list lock is already held by this thread. |
| 1170 | * If it's not, we may be living dangerously if we have to scan through |
| 1171 | * the thread list to find a match. (The VM will generally be in a |
| 1172 | * suspended state when executing here, so this is a minor concern |
| 1173 | * unless we're dumping while threads are running, in which case there's |
| 1174 | * a good chance of stuff blowing up anyway.) |
| 1175 | */ |
| 1176 | *pOwner = dvmGetObjectLockHolder(obj); |
| 1177 | |
| 1178 | return true; |
| 1179 | } |
| 1180 | |
| 1181 | /* |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1182 | * Dump stack frames, starting from the specified frame and moving down. |
| 1183 | * |
| 1184 | * Each frame holds a pointer to the currently executing method, and the |
| 1185 | * saved program counter from the caller ("previous" frame). This means |
| 1186 | * we don't have the PC for the current method on the stack, which is |
| 1187 | * pretty reasonable since it's in the "PC register" for the VM. Because |
| 1188 | * exceptions need to show the correct line number we actually *do* have |
| 1189 | * an updated version in the fame's "xtra.currentPc", but it's unreliable. |
| 1190 | * |
| 1191 | * Note "framePtr" could be NULL in rare circumstances. |
| 1192 | */ |
| 1193 | static void dumpFrames(const DebugOutputTarget* target, void* framePtr, |
| 1194 | Thread* thread) |
| 1195 | { |
| 1196 | const StackSaveArea* saveArea; |
| 1197 | const Method* method; |
| 1198 | int checkCount = 0; |
| 1199 | const u2* currentPc = NULL; |
| 1200 | bool first = true; |
| 1201 | |
| 1202 | /* |
| Andy McFadden | d19988d | 2010-10-22 13:32:12 -0700 | [diff] [blame] | 1203 | * We call functions that require us to be holding the thread list lock. |
| 1204 | * It's probable that the caller has already done so, but it's not |
| 1205 | * guaranteed. If it's not locked, lock it now. |
| 1206 | */ |
| 1207 | bool needThreadUnlock = dvmTryLockThreadList(); |
| 1208 | |
| 1209 | /* |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1210 | * The "currentPc" is updated whenever we execute an instruction that |
| 1211 | * might throw an exception. Show it here. |
| 1212 | */ |
| 1213 | if (framePtr != NULL && !dvmIsBreakFrame(framePtr)) { |
| 1214 | saveArea = SAVEAREA_FROM_FP(framePtr); |
| 1215 | |
| 1216 | if (saveArea->xtra.currentPc != NULL) |
| 1217 | currentPc = saveArea->xtra.currentPc; |
| 1218 | } |
| 1219 | |
| 1220 | while (framePtr != NULL) { |
| 1221 | saveArea = SAVEAREA_FROM_FP(framePtr); |
| 1222 | method = saveArea->method; |
| 1223 | |
| 1224 | if (dvmIsBreakFrame(framePtr)) { |
| 1225 | //dvmPrintDebugMessage(target, " (break frame)\n"); |
| 1226 | } else { |
| 1227 | int relPc; |
| 1228 | |
| 1229 | if (currentPc != NULL) |
| 1230 | relPc = currentPc - saveArea->method->insns; |
| 1231 | else |
| 1232 | relPc = -1; |
| 1233 | |
| Elliott Hughes | 5016966 | 2010-11-22 13:14:23 -0800 | [diff] [blame] | 1234 | char* className = dvmHumanReadableDescriptor(method->clazz->descriptor); |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1235 | if (dvmIsNativeMethod(method)) |
| 1236 | dvmPrintDebugMessage(target, |
| 1237 | " at %s.%s(Native Method)\n", className, method->name); |
| 1238 | else { |
| 1239 | dvmPrintDebugMessage(target, |
| 1240 | " at %s.%s(%s:%s%d)\n", |
| 1241 | className, method->name, dvmGetMethodSourceFile(method), |
| 1242 | (relPc >= 0 && first) ? "~" : "", |
| 1243 | relPc < 0 ? -1 : dvmLineNumFromPC(method, relPc)); |
| 1244 | } |
| 1245 | free(className); |
| 1246 | |
| Andy McFadden | fd54266 | 2010-03-12 13:39:59 -0800 | [diff] [blame] | 1247 | if (first) { |
| 1248 | /* |
| 1249 | * Decorate WAIT and MONITOR threads with some detail on |
| 1250 | * the first frame. |
| 1251 | * |
| 1252 | * warning: wait status not stable, even in suspend |
| 1253 | */ |
| 1254 | if (thread->status == THREAD_WAIT || |
| 1255 | thread->status == THREAD_TIMED_WAIT) |
| 1256 | { |
| 1257 | Monitor* mon = thread->waitMonitor; |
| 1258 | Object* obj = dvmGetMonitorObject(mon); |
| 1259 | if (obj != NULL) { |
| Andy McFadden | d19988d | 2010-10-22 13:32:12 -0700 | [diff] [blame] | 1260 | Thread* joinThread = NULL; |
| Elliott Hughes | 5016966 | 2010-11-22 13:14:23 -0800 | [diff] [blame] | 1261 | className = dvmHumanReadableDescriptor(obj->clazz->descriptor); |
| Andy McFadden | d19988d | 2010-10-22 13:32:12 -0700 | [diff] [blame] | 1262 | if (strcmp(className, "java.lang.VMThread") == 0) { |
| 1263 | joinThread = dvmGetThreadFromThreadObject(obj); |
| 1264 | } |
| 1265 | if (joinThread == NULL) { |
| 1266 | dvmPrintDebugMessage(target, |
| 1267 | " - waiting on <%p> (a %s)\n", obj, className); |
| 1268 | } else { |
| 1269 | dvmPrintDebugMessage(target, |
| 1270 | " - waiting on <%p> (a %s) tid=%d\n", |
| 1271 | obj, className, joinThread->threadId); |
| 1272 | } |
| Andy McFadden | fd54266 | 2010-03-12 13:39:59 -0800 | [diff] [blame] | 1273 | free(className); |
| 1274 | } |
| 1275 | } else if (thread->status == THREAD_MONITOR) { |
| 1276 | Object* obj; |
| 1277 | Thread* owner; |
| 1278 | if (extractMonitorEnterObject(thread, &obj, &owner)) { |
| Elliott Hughes | 5016966 | 2010-11-22 13:14:23 -0800 | [diff] [blame] | 1279 | className = dvmHumanReadableDescriptor(obj->clazz->descriptor); |
| Andy McFadden | fd54266 | 2010-03-12 13:39:59 -0800 | [diff] [blame] | 1280 | if (owner != NULL) { |
| 1281 | char* threadName = dvmGetThreadName(owner); |
| 1282 | dvmPrintDebugMessage(target, |
| 1283 | " - waiting to lock <%p> (a %s) held by threadid=%d (%s)\n", |
| 1284 | obj, className, owner->threadId, threadName); |
| 1285 | free(threadName); |
| 1286 | } else { |
| 1287 | dvmPrintDebugMessage(target, |
| 1288 | " - waiting to lock <%p> (a %s) held by ???\n", |
| 1289 | obj, className); |
| 1290 | } |
| 1291 | free(className); |
| 1292 | } |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1293 | } |
| 1294 | } |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1295 | } |
| 1296 | |
| 1297 | /* |
| 1298 | * Get saved PC for previous frame. There's no savedPc in a "break" |
| 1299 | * frame, because that represents native or interpreted code |
| 1300 | * invoked by the VM. The saved PC is sitting in the "PC register", |
| 1301 | * a local variable on the native stack. |
| 1302 | */ |
| 1303 | currentPc = saveArea->savedPc; |
| 1304 | |
| 1305 | first = false; |
| 1306 | |
| Andy McFadden | 0083d37 | 2009-08-21 14:44:04 -0700 | [diff] [blame] | 1307 | if (saveArea->prevFrame != NULL && saveArea->prevFrame <= framePtr) { |
| 1308 | LOGW("Warning: loop in stack trace at frame %d (%p -> %p)\n", |
| 1309 | checkCount, framePtr, saveArea->prevFrame); |
| 1310 | break; |
| 1311 | } |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1312 | framePtr = saveArea->prevFrame; |
| 1313 | |
| 1314 | checkCount++; |
| Andy McFadden | 0083d37 | 2009-08-21 14:44:04 -0700 | [diff] [blame] | 1315 | if (checkCount > 300) { |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1316 | dvmPrintDebugMessage(target, |
| 1317 | " ***** printed %d frames, not showing any more\n", |
| 1318 | checkCount); |
| 1319 | break; |
| 1320 | } |
| 1321 | } |
| 1322 | dvmPrintDebugMessage(target, "\n"); |
| Andy McFadden | d19988d | 2010-10-22 13:32:12 -0700 | [diff] [blame] | 1323 | |
| 1324 | if (needThreadUnlock) { |
| 1325 | dvmUnlockThreadList(); |
| 1326 | } |
| The Android Open Source Project | f6c3871 | 2009-03-03 19:28:47 -0800 | [diff] [blame] | 1327 | } |
| 1328 | |
| 1329 | |
| 1330 | /* |
| 1331 | * Dump the stack for the specified thread. |
| 1332 | */ |
| 1333 | void dvmDumpThreadStack(const DebugOutputTarget* target, Thread* thread) |
| 1334 | { |
| 1335 | dumpFrames(target, thread->curFrame, thread); |
| 1336 | } |
| 1337 | |
| 1338 | /* |
| 1339 | * Dump the stack for the specified thread, which is still running. |
| 1340 | * |
| 1341 | * This is very dangerous, because stack frames are being pushed on and |
| 1342 | * popped off, and if the thread exits we'll be looking at freed memory. |
| 1343 | * The plan here is to take a snapshot of the stack and then dump that |
| 1344 | * to try to minimize the chances of catching it mid-update. This should |
| 1345 | * work reasonably well on a single-CPU system. |
| 1346 | * |
| 1347 | * There is a small chance that calling here will crash the VM. |
| 1348 | */ |
| 1349 | void dvmDumpRunningThreadStack(const DebugOutputTarget* target, Thread* thread) |
| 1350 | { |
| 1351 | StackSaveArea* saveArea; |
| 1352 | const u1* origStack; |
| 1353 | u1* stackCopy = NULL; |
| 1354 | int origSize, fpOffset; |
| 1355 | void* fp; |
| 1356 | int depthLimit = 200; |
| 1357 | |
| 1358 | if (thread == NULL || thread->curFrame == NULL) { |
| 1359 | dvmPrintDebugMessage(target, |
| 1360 | "DumpRunning: Thread at %p has no curFrame (threadid=%d)\n", |
| 1361 | thread, (thread != NULL) ? thread->threadId : 0); |
| 1362 | return; |
| 1363 | } |
| 1364 | |
| 1365 | /* wait for a full quantum */ |
| 1366 | sched_yield(); |
| 1367 | |
| 1368 | /* copy the info we need, then the stack itself */ |
| 1369 | origSize = thread->interpStackSize; |
| 1370 | origStack = (const u1*) thread->interpStackStart - origSize; |
| 1371 | stackCopy = (u1*) malloc(origSize); |
| 1372 | fpOffset = (u1*) thread->curFrame - origStack; |
| 1373 | memcpy(stackCopy, origStack, origSize); |
| 1374 | |
| 1375 | /* |
| 1376 | * Run through the stack and rewrite the "prev" pointers. |
| 1377 | */ |
| 1378 | //LOGI("DR: fpOff=%d (from %p %p)\n",fpOffset, origStack, thread->curFrame); |
| 1379 | fp = stackCopy + fpOffset; |
| 1380 | while (true) { |
| 1381 | int prevOffset; |
| 1382 | |
| 1383 | if (depthLimit-- < 0) { |
| 1384 | /* we're probably screwed */ |
| 1385 | dvmPrintDebugMessage(target, "DumpRunning: depth limit hit\n"); |
| 1386 | dvmAbort(); |
| 1387 | } |
| 1388 | saveArea = SAVEAREA_FROM_FP(fp); |
| 1389 | if (saveArea->prevFrame == NULL) |
| 1390 | break; |
| 1391 | |
| 1392 | prevOffset = (u1*) saveArea->prevFrame - origStack; |
| 1393 | if (prevOffset < 0 || prevOffset > origSize) { |
| 1394 | dvmPrintDebugMessage(target, |
| 1395 | "DumpRunning: bad offset found: %d (from %p %p)\n", |
| 1396 | prevOffset, origStack, saveArea->prevFrame); |
| 1397 | saveArea->prevFrame = NULL; |
| 1398 | break; |
| 1399 | } |
| 1400 | |
| 1401 | saveArea->prevFrame = stackCopy + prevOffset; |
| 1402 | fp = saveArea->prevFrame; |
| 1403 | } |
| 1404 | |
| 1405 | /* |
| 1406 | * We still need to pass the Thread for some monitor wait stuff. |
| 1407 | */ |
| 1408 | dumpFrames(target, stackCopy + fpOffset, thread); |
| 1409 | free(stackCopy); |
| 1410 | } |