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