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 * dalvik.system.VMDebug 19 */ 20 #include "Dalvik.h" 21 #include "native/InternalNativePriv.h" 22 23 #include <string.h> 24 #include <unistd.h> 25 #include <errno.h> 26 27 28 /* 29 * Extracts the fd from a FileDescriptor object. 30 * 31 * If an error is encountered, or the extracted descriptor is numerically 32 * invalid, this returns -1 with an exception raised. 33 */ 34 static int getFileDescriptor(Object* obj) 35 { 36 assert(obj != NULL); 37 assert(strcmp(obj->clazz->descriptor, "Ljava/io/FileDescriptor;") == 0); 38 39 InstField* field = dvmFindInstanceField(obj->clazz, "descriptor", "I"); 40 if (field == NULL) { 41 dvmThrowException("Ljava/lang/NoSuchFieldException;", 42 "No FileDescriptor.descriptor field"); 43 return -1; 44 } 45 46 int fd = dvmGetFieldInt(obj, field->byteOffset); 47 if (fd < 0) { 48 dvmThrowExceptionFmt("Ljava/lang/RuntimeException;", 49 "Invalid file descriptor"); 50 return -1; 51 } 52 53 return fd; 54 } 55 56 /* 57 * Convert an array of char* into a String[]. 58 * 59 * Returns NULL on failure, with an exception raised. 60 */ 61 static ArrayObject* convertStringArray(char** strings, size_t count) 62 { 63 Thread* self = dvmThreadSelf(); 64 65 /* 66 * Allocate an array to hold the String objects. 67 */ 68 ClassObject* stringArrayClass = 69 dvmFindArrayClass("[Ljava/lang/String;", NULL); 70 if (stringArrayClass == NULL) { 71 /* shouldn't happen */ 72 LOGE("Unable to find [Ljava/lang/String;\n"); 73 dvmAbort(); 74 } 75 76 ArrayObject* stringArray = 77 dvmAllocArrayByClass(stringArrayClass, count, ALLOC_DEFAULT); 78 if (stringArray == NULL) { 79 /* probably OOM */ 80 LOGD("Failed allocating array of %d strings\n", count); 81 assert(dvmCheckException(self)); 82 return NULL; 83 } 84 85 /* 86 * Create the individual String objects and add them to the array. 87 */ 88 size_t i; 89 for (i = 0; i < count; i++) { 90 Object *str = 91 (Object *)dvmCreateStringFromCstr(strings[i]); 92 if (str == NULL) { 93 /* probably OOM; drop out now */ 94 assert(dvmCheckException(self)); 95 dvmReleaseTrackedAlloc((Object*)stringArray, self); 96 return NULL; 97 } 98 dvmSetObjectArrayElement(stringArray, i, str); 99 /* stored in tracked array, okay to release */ 100 dvmReleaseTrackedAlloc(str, self); 101 } 102 103 dvmReleaseTrackedAlloc((Object*)stringArray, self); 104 return stringArray; 105 } 106 107 /* 108 * static String[] getVmFeatureList() 109 * 110 * Return a set of strings describing available VM features (this is chiefly 111 * of interest to DDMS). Some features may be controlled by compile-time 112 * or command-line flags. 113 */ 114 static void Dalvik_dalvik_system_VMDebug_getVmFeatureList(const u4* args, 115 JValue* pResult) 116 { 117 static const int MAX_FEATURE_COUNT = 10; 118 char* features[MAX_FEATURE_COUNT]; 119 int idx = 0; 120 121 /* VM responds to DDMS method profiling requests */ 122 features[idx++] = "method-trace-profiling"; 123 features[idx++] = "method-trace-profiling-streaming"; 124 #ifdef WITH_HPROF 125 /* VM responds to DDMS heap dump requests */ 126 features[idx++] = "hprof-heap-dump"; 127 features[idx++] = "hprof-heap-dump-streaming"; 128 #endif 129 130 assert(idx <= MAX_FEATURE_COUNT); 131 132 LOGV("+++ sending up %d features\n", idx); 133 ArrayObject* arrayObj = convertStringArray(features, idx); 134 RETURN_PTR(arrayObj); /* will be null on OOM */ 135 } 136 137 138 /* These must match the values in dalvik.system.VMDebug. 139 */ 140 enum { 141 KIND_ALLOCATED_OBJECTS = 1<<0, 142 KIND_ALLOCATED_BYTES = 1<<1, 143 KIND_FREED_OBJECTS = 1<<2, 144 KIND_FREED_BYTES = 1<<3, 145 KIND_GC_INVOCATIONS = 1<<4, 146 KIND_CLASS_INIT_COUNT = 1<<5, 147 KIND_CLASS_INIT_TIME = 1<<6, 148 #if PROFILE_EXTERNAL_ALLOCATIONS 149 KIND_EXT_ALLOCATED_OBJECTS = 1<<12, 150 KIND_EXT_ALLOCATED_BYTES = 1<<13, 151 KIND_EXT_FREED_OBJECTS = 1<<14, 152 KIND_EXT_FREED_BYTES = 1<<15, 153 #endif // PROFILE_EXTERNAL_ALLOCATIONS 154 155 KIND_GLOBAL_ALLOCATED_OBJECTS = KIND_ALLOCATED_OBJECTS, 156 KIND_GLOBAL_ALLOCATED_BYTES = KIND_ALLOCATED_BYTES, 157 KIND_GLOBAL_FREED_OBJECTS = KIND_FREED_OBJECTS, 158 KIND_GLOBAL_FREED_BYTES = KIND_FREED_BYTES, 159 KIND_GLOBAL_GC_INVOCATIONS = KIND_GC_INVOCATIONS, 160 KIND_GLOBAL_CLASS_INIT_COUNT = KIND_CLASS_INIT_COUNT, 161 KIND_GLOBAL_CLASS_INIT_TIME = KIND_CLASS_INIT_TIME, 162 #if PROFILE_EXTERNAL_ALLOCATIONS 163 KIND_GLOBAL_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS, 164 KIND_GLOBAL_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES, 165 KIND_GLOBAL_EXT_FREED_OBJECTS = KIND_EXT_FREED_OBJECTS, 166 KIND_GLOBAL_EXT_FREED_BYTES = KIND_EXT_FREED_BYTES, 167 #endif // PROFILE_EXTERNAL_ALLOCATIONS 168 169 KIND_THREAD_ALLOCATED_OBJECTS = KIND_ALLOCATED_OBJECTS << 16, 170 KIND_THREAD_ALLOCATED_BYTES = KIND_ALLOCATED_BYTES << 16, 171 KIND_THREAD_FREED_OBJECTS = KIND_FREED_OBJECTS << 16, 172 KIND_THREAD_FREED_BYTES = KIND_FREED_BYTES << 16, 173 #if PROFILE_EXTERNAL_ALLOCATIONS 174 KIND_THREAD_EXT_ALLOCATED_OBJECTS = KIND_EXT_ALLOCATED_OBJECTS << 16, 175 KIND_THREAD_EXT_ALLOCATED_BYTES = KIND_EXT_ALLOCATED_BYTES << 16, 176 KIND_THREAD_EXT_FREED_OBJECTS = KIND_EXT_FREED_OBJECTS << 16, 177 KIND_THREAD_EXT_FREED_BYTES = KIND_EXT_FREED_BYTES << 16, 178 #endif // PROFILE_EXTERNAL_ALLOCATIONS 179 KIND_THREAD_GC_INVOCATIONS = KIND_GC_INVOCATIONS << 16, 180 181 // TODO: failedAllocCount, failedAllocSize 182 }; 183 184 #define KIND_ALL_COUNTS 0xffffffff 185 186 /* 187 * Zero out the specified fields. 188 */ 189 static void clearAllocProfStateFields(AllocProfState *allocProf, 190 unsigned int kinds) 191 { 192 if (kinds & KIND_ALLOCATED_OBJECTS) { 193 allocProf->allocCount = 0; 194 } 195 if (kinds & KIND_ALLOCATED_BYTES) { 196 allocProf->allocSize = 0; 197 } 198 if (kinds & KIND_FREED_OBJECTS) { 199 allocProf->freeCount = 0; 200 } 201 if (kinds & KIND_FREED_BYTES) { 202 allocProf->freeSize = 0; 203 } 204 if (kinds & KIND_GC_INVOCATIONS) { 205 allocProf->gcCount = 0; 206 } 207 if (kinds & KIND_CLASS_INIT_COUNT) { 208 allocProf->classInitCount = 0; 209 } 210 if (kinds & KIND_CLASS_INIT_TIME) { 211 allocProf->classInitTime = 0; 212 } 213 #if PROFILE_EXTERNAL_ALLOCATIONS 214 if (kinds & KIND_EXT_ALLOCATED_OBJECTS) { 215 allocProf->externalAllocCount = 0; 216 } 217 if (kinds & KIND_EXT_ALLOCATED_BYTES) { 218 allocProf->externalAllocSize = 0; 219 } 220 if (kinds & KIND_EXT_FREED_OBJECTS) { 221 allocProf->externalFreeCount = 0; 222 } 223 if (kinds & KIND_EXT_FREED_BYTES) { 224 allocProf->externalFreeSize = 0; 225 } 226 #endif // PROFILE_EXTERNAL_ALLOCATIONS 227 } 228 229 /* 230 * static void startAllocCounting() 231 * 232 * Reset the counters and enable counting. 233 * 234 * TODO: this currently only resets the per-thread counters for the current 235 * thread. If we actually start using the per-thread counters we'll 236 * probably want to fix this. 237 */ 238 static void Dalvik_dalvik_system_VMDebug_startAllocCounting(const u4* args, 239 JValue* pResult) 240 { 241 UNUSED_PARAMETER(args); 242 243 clearAllocProfStateFields(&gDvm.allocProf, KIND_ALL_COUNTS); 244 clearAllocProfStateFields(&dvmThreadSelf()->allocProf, KIND_ALL_COUNTS); 245 dvmStartAllocCounting(); 246 RETURN_VOID(); 247 } 248 249 /* 250 * public static void stopAllocCounting() 251 */ 252 static void Dalvik_dalvik_system_VMDebug_stopAllocCounting(const u4* args, 253 JValue* pResult) 254 { 255 UNUSED_PARAMETER(args); 256 257 dvmStopAllocCounting(); 258 RETURN_VOID(); 259 } 260 261 /* 262 * private static int getAllocCount(int kind) 263 */ 264 static void Dalvik_dalvik_system_VMDebug_getAllocCount(const u4* args, 265 JValue* pResult) 266 { 267 AllocProfState *allocProf; 268 unsigned int kind = args[0]; 269 if (kind < (1<<16)) { 270 allocProf = &gDvm.allocProf; 271 } else { 272 allocProf = &dvmThreadSelf()->allocProf; 273 kind >>= 16; 274 } 275 switch (kind) { 276 case KIND_ALLOCATED_OBJECTS: 277 pResult->i = allocProf->allocCount; 278 break; 279 case KIND_ALLOCATED_BYTES: 280 pResult->i = allocProf->allocSize; 281 break; 282 case KIND_FREED_OBJECTS: 283 pResult->i = allocProf->freeCount; 284 break; 285 case KIND_FREED_BYTES: 286 pResult->i = allocProf->freeSize; 287 break; 288 case KIND_GC_INVOCATIONS: 289 pResult->i = allocProf->gcCount; 290 break; 291 case KIND_CLASS_INIT_COUNT: 292 pResult->i = allocProf->classInitCount; 293 break; 294 case KIND_CLASS_INIT_TIME: 295 /* convert nsec to usec, reduce to 32 bits */ 296 pResult->i = (int) (allocProf->classInitTime / 1000); 297 break; 298 #if PROFILE_EXTERNAL_ALLOCATIONS 299 case KIND_EXT_ALLOCATED_OBJECTS: 300 pResult->i = allocProf->externalAllocCount; 301 break; 302 case KIND_EXT_ALLOCATED_BYTES: 303 pResult->i = allocProf->externalAllocSize; 304 break; 305 case KIND_EXT_FREED_OBJECTS: 306 pResult->i = allocProf->externalFreeCount; 307 break; 308 case KIND_EXT_FREED_BYTES: 309 pResult->i = allocProf->externalFreeSize; 310 break; 311 #endif // PROFILE_EXTERNAL_ALLOCATIONS 312 default: 313 assert(false); 314 pResult->i = -1; 315 } 316 } 317 318 /* 319 * public static void resetAllocCount(int kinds) 320 */ 321 static void Dalvik_dalvik_system_VMDebug_resetAllocCount(const u4* args, 322 JValue* pResult) 323 { 324 unsigned int kinds = args[0]; 325 clearAllocProfStateFields(&gDvm.allocProf, kinds & 0xffff); 326 clearAllocProfStateFields(&dvmThreadSelf()->allocProf, kinds >> 16); 327 RETURN_VOID(); 328 } 329 330 /* 331 * static void startMethodTracingNative(String traceFileName, 332 * FileDescriptor fd, int bufferSize, int flags) 333 * 334 * Start method trace profiling. 335 * 336 * If both "traceFileName" and "fd" are null, the result will be sent 337 * directly to DDMS. (The non-DDMS versions of the calls are expected 338 * to enforce non-NULL filenames.) 339 */ 340 static void Dalvik_dalvik_system_VMDebug_startMethodTracingNative(const u4* args, 341 JValue* pResult) 342 { 343 StringObject* traceFileStr = (StringObject*) args[0]; 344 Object* traceFd = (Object*) args[1]; 345 int bufferSize = args[2]; 346 int flags = args[3]; 347 348 if (bufferSize == 0) { 349 // Default to 8MB per the documentation. 350 bufferSize = 8 * 1024 * 1024; 351 } 352 353 if (bufferSize < 1024) { 354 dvmThrowException("Ljava/lang/IllegalArgumentException;", NULL); 355 RETURN_VOID(); 356 } 357 358 char* traceFileName = NULL; 359 if (traceFileStr != NULL) 360 traceFileName = dvmCreateCstrFromString(traceFileStr); 361 362 int fd = -1; 363 if (traceFd != NULL) { 364 int origFd = getFileDescriptor(traceFd); 365 if (origFd < 0) 366 RETURN_VOID(); 367 368 fd = dup(origFd); 369 if (fd < 0) { 370 dvmThrowExceptionFmt("Ljava/lang/RuntimeException;", 371 "dup(%d) failed: %s", origFd, strerror(errno)); 372 RETURN_VOID(); 373 } 374 } 375 376 dvmMethodTraceStart(traceFileName != NULL ? traceFileName : "[DDMS]", 377 fd, bufferSize, flags, (traceFileName == NULL && fd == -1)); 378 free(traceFileName); 379 RETURN_VOID(); 380 } 381 382 /* 383 * static boolean isMethodTracingActive() 384 * 385 * Determine whether method tracing is currently active. 386 */ 387 static void Dalvik_dalvik_system_VMDebug_isMethodTracingActive(const u4* args, 388 JValue* pResult) 389 { 390 UNUSED_PARAMETER(args); 391 392 RETURN_BOOLEAN(dvmIsMethodTraceActive()); 393 } 394 395 /* 396 * static void stopMethodTracing() 397 * 398 * Stop method tracing. 399 */ 400 static void Dalvik_dalvik_system_VMDebug_stopMethodTracing(const u4* args, 401 JValue* pResult) 402 { 403 UNUSED_PARAMETER(args); 404 405 dvmMethodTraceStop(); 406 RETURN_VOID(); 407 } 408 409 /* 410 * static void startEmulatorTracing() 411 * 412 * Start sending method trace info to the emulator. 413 */ 414 static void Dalvik_dalvik_system_VMDebug_startEmulatorTracing(const u4* args, 415 JValue* pResult) 416 { 417 UNUSED_PARAMETER(args); 418 419 dvmEmulatorTraceStart(); 420 RETURN_VOID(); 421 } 422 423 /* 424 * static void stopEmulatorTracing() 425 * 426 * Start sending method trace info to the emulator. 427 */ 428 static void Dalvik_dalvik_system_VMDebug_stopEmulatorTracing(const u4* args, 429 JValue* pResult) 430 { 431 UNUSED_PARAMETER(args); 432 433 dvmEmulatorTraceStop(); 434 RETURN_VOID(); 435 } 436 437 /* 438 * static int setAllocationLimit(int limit) 439 * 440 * Set the current allocation limit in this thread. Return the previous 441 * value. 442 */ 443 static void Dalvik_dalvik_system_VMDebug_setAllocationLimit(const u4* args, 444 JValue* pResult) 445 { 446 #if defined(WITH_ALLOC_LIMITS) 447 gDvm.checkAllocLimits = true; 448 449 Thread* self = dvmThreadSelf(); 450 int newLimit = args[0]; 451 int oldLimit = self->allocLimit; 452 453 if (newLimit < -1) { 454 LOGE("WARNING: bad limit request (%d)\n", newLimit); 455 newLimit = -1; 456 } 457 self->allocLimit = newLimit; 458 RETURN_INT(oldLimit); 459 #else 460 UNUSED_PARAMETER(args); 461 RETURN_INT(-1); 462 #endif 463 } 464 465 /* 466 * static int setGlobalAllocationLimit(int limit) 467 * 468 * Set the allocation limit for this process. Returns the previous value. 469 */ 470 static void Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit(const u4* args, 471 JValue* pResult) 472 { 473 #if defined(WITH_ALLOC_LIMITS) 474 gDvm.checkAllocLimits = true; 475 476 int newLimit = args[0]; 477 int oldLimit = gDvm.allocationLimit; 478 479 if (newLimit < -1 || newLimit > 0) { 480 LOGE("WARNING: bad limit request (%d)\n", newLimit); 481 newLimit = -1; 482 } 483 // TODO: should use an atomic swap here 484 gDvm.allocationLimit = newLimit; 485 RETURN_INT(oldLimit); 486 #else 487 UNUSED_PARAMETER(args); 488 RETURN_INT(-1); 489 #endif 490 } 491 492 /* 493 * static boolean isDebuggerConnected() 494 * 495 * Returns "true" if a debugger is attached. 496 */ 497 static void Dalvik_dalvik_system_VMDebug_isDebuggerConnected(const u4* args, 498 JValue* pResult) 499 { 500 UNUSED_PARAMETER(args); 501 502 RETURN_BOOLEAN(dvmDbgIsDebuggerConnected()); 503 } 504 505 /* 506 * static boolean isDebuggingEnabled() 507 * 508 * Returns "true" if debugging is enabled. 509 */ 510 static void Dalvik_dalvik_system_VMDebug_isDebuggingEnabled(const u4* args, 511 JValue* pResult) 512 { 513 UNUSED_PARAMETER(args); 514 515 RETURN_BOOLEAN(gDvm.jdwpConfigured); 516 } 517 518 /* 519 * static long lastDebuggerActivity() 520 * 521 * Returns the time, in msec, since we last had an interaction with the 522 * debugger (send or receive). 523 */ 524 static void Dalvik_dalvik_system_VMDebug_lastDebuggerActivity(const u4* args, 525 JValue* pResult) 526 { 527 UNUSED_PARAMETER(args); 528 529 RETURN_LONG(dvmDbgLastDebuggerActivity()); 530 } 531 532 /* 533 * static void startInstructionCounting() 534 */ 535 static void Dalvik_dalvik_system_VMDebug_startInstructionCounting(const u4* args, 536 JValue* pResult) 537 { 538 dvmStartInstructionCounting(); 539 RETURN_VOID(); 540 } 541 542 /* 543 * static void stopInstructionCounting() 544 */ 545 static void Dalvik_dalvik_system_VMDebug_stopInstructionCounting(const u4* args, 546 JValue* pResult) 547 { 548 dvmStopInstructionCounting(); 549 RETURN_VOID(); 550 } 551 552 /* 553 * static boolean getInstructionCount(int[] counts) 554 * 555 * Grab a copy of the global instruction count array. 556 * 557 * Since the instruction counts aren't synchronized, we use sched_yield 558 * to improve our chances of finishing without contention. (Only makes 559 * sense on a uniprocessor.) 560 */ 561 static void Dalvik_dalvik_system_VMDebug_getInstructionCount(const u4* args, 562 JValue* pResult) 563 { 564 ArrayObject* countArray = (ArrayObject*) args[0]; 565 int* storage; 566 567 storage = (int*) countArray->contents; 568 sched_yield(); 569 memcpy(storage, gDvm.executedInstrCounts, 570 kNumDalvikInstructions * sizeof(int)); 571 RETURN_VOID(); 572 } 573 574 /* 575 * static boolean resetInstructionCount() 576 * 577 * Reset the instruction count array. 578 */ 579 static void Dalvik_dalvik_system_VMDebug_resetInstructionCount(const u4* args, 580 JValue* pResult) 581 { 582 sched_yield(); 583 memset(gDvm.executedInstrCounts, 0, kNumDalvikInstructions * sizeof(int)); 584 RETURN_VOID(); 585 } 586 587 /* 588 * static void printLoadedClasses(int flags) 589 * 590 * Dump the list of loaded classes. 591 */ 592 static void Dalvik_dalvik_system_VMDebug_printLoadedClasses(const u4* args, 593 JValue* pResult) 594 { 595 int flags = args[0]; 596 597 dvmDumpAllClasses(flags); 598 599 RETURN_VOID(); 600 } 601 602 /* 603 * static int getLoadedClassCount() 604 * 605 * Return the number of loaded classes 606 */ 607 static void Dalvik_dalvik_system_VMDebug_getLoadedClassCount(const u4* args, 608 JValue* pResult) 609 { 610 int count; 611 612 UNUSED_PARAMETER(args); 613 614 count = dvmGetNumLoadedClasses(); 615 616 RETURN_INT(count); 617 } 618 619 /* 620 * Returns the thread-specific CPU-time clock value for the current thread, 621 * or -1 if the feature isn't supported. 622 */ 623 static void Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos(const u4* args, 624 JValue* pResult) 625 { 626 jlong result; 627 628 #ifdef HAVE_POSIX_CLOCKS 629 struct timespec now; 630 clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now); 631 result = (jlong) (now.tv_sec*1000000000LL + now.tv_nsec); 632 #else 633 result = (jlong) -1; 634 #endif 635 636 RETURN_LONG(result); 637 } 638 639 /* 640 * static void dumpHprofData(String fileName, FileDescriptor fd) 641 * 642 * Cause "hprof" data to be dumped. We can throw an IOException if an 643 * error occurs during file handling. 644 */ 645 static void Dalvik_dalvik_system_VMDebug_dumpHprofData(const u4* args, 646 JValue* pResult) 647 { 648 #ifdef WITH_HPROF 649 StringObject* fileNameStr = (StringObject*) args[0]; 650 Object* fileDescriptor = (Object*) args[1]; 651 char* fileName; 652 int result; 653 654 /* 655 * Only one of these may be NULL. 656 */ 657 if (fileNameStr == NULL && fileDescriptor == NULL) { 658 dvmThrowException("Ljava/lang/NullPointerException;", NULL); 659 RETURN_VOID(); 660 } 661 662 if (fileNameStr != NULL) { 663 fileName = dvmCreateCstrFromString(fileNameStr); 664 if (fileName == NULL) { 665 /* unexpected -- malloc failure? */ 666 dvmThrowException("Ljava/lang/RuntimeException;", "malloc failure?"); 667 RETURN_VOID(); 668 } 669 } else { 670 fileName = strdup("[fd]"); 671 } 672 673 int fd = -1; 674 if (fileDescriptor != NULL) { 675 fd = getFileDescriptor(fileDescriptor); 676 if (fd < 0) 677 RETURN_VOID(); 678 } 679 680 result = hprofDumpHeap(fileName, fd, false); 681 free(fileName); 682 683 if (result != 0) { 684 /* ideally we'd throw something more specific based on actual failure */ 685 dvmThrowException("Ljava/lang/RuntimeException;", 686 "Failure during heap dump -- check log output for details"); 687 RETURN_VOID(); 688 } 689 #else 690 dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); 691 #endif 692 693 RETURN_VOID(); 694 } 695 696 /* 697 * static void dumpHprofDataDdms() 698 * 699 * Cause "hprof" data to be computed and sent directly to DDMS. 700 */ 701 static void Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms(const u4* args, 702 JValue* pResult) 703 { 704 #ifdef WITH_HPROF 705 int result; 706 707 result = hprofDumpHeap("[DDMS]", -1, true); 708 709 if (result != 0) { 710 /* ideally we'd throw something more specific based on actual failure */ 711 dvmThrowException("Ljava/lang/RuntimeException;", 712 "Failure during heap dump -- check log output for details"); 713 RETURN_VOID(); 714 } 715 #else 716 dvmThrowException("Ljava/lang/UnsupportedOperationException;", NULL); 717 #endif 718 719 RETURN_VOID(); 720 } 721 722 /* 723 * static boolean cacheRegisterMap(String classAndMethodDescr) 724 * 725 * If the specified class is loaded, and the named method exists, ensure 726 * that the method's register map is ready for use. If the class/method 727 * cannot be found, nothing happens. 728 * 729 * This can improve the zygote's sharing of compressed register maps. Do 730 * this after class preloading. 731 * 732 * Returns true if the register map is cached and ready, either as a result 733 * of this call or earlier activity. Returns false if the class isn't loaded, 734 * if the method couldn't be found, or if the method has no register map. 735 * 736 * (Uncomment logs in dvmGetExpandedRegisterMap0() to gather stats.) 737 */ 738 static void Dalvik_dalvik_system_VMDebug_cacheRegisterMap(const u4* args, 739 JValue* pResult) 740 { 741 StringObject* classAndMethodDescStr = (StringObject*) args[0]; 742 ClassObject* clazz; 743 bool result = false; 744 745 if (classAndMethodDescStr == NULL) { 746 dvmThrowException("Ljava/lang/NullPointerException;", NULL); 747 RETURN_VOID(); 748 } 749 750 char* classAndMethodDesc = NULL; 751 752 /* 753 * Pick the string apart. We have a local copy, so just modify it 754 * in place. 755 */ 756 classAndMethodDesc = dvmCreateCstrFromString(classAndMethodDescStr); 757 758 char* methodName = strchr(classAndMethodDesc, '.'); 759 if (methodName == NULL) { 760 dvmThrowException("Ljava/lang/RuntimeException;", 761 "method name not found in string"); 762 RETURN_VOID(); 763 } 764 *methodName++ = '\0'; 765 766 char* methodDescr = strchr(methodName, ':'); 767 if (methodDescr == NULL) { 768 dvmThrowException("Ljava/lang/RuntimeException;", 769 "method descriptor not found in string"); 770 RETURN_VOID(); 771 } 772 *methodDescr++ = '\0'; 773 774 //LOGD("GOT: %s %s %s\n", classAndMethodDesc, methodName, methodDescr); 775 776 /* 777 * Find the class, but only if it's already loaded. 778 */ 779 clazz = dvmLookupClass(classAndMethodDesc, NULL, false); 780 if (clazz == NULL) { 781 LOGD("Class %s not found in bootstrap loader\n", classAndMethodDesc); 782 goto bail; 783 } 784 785 Method* method; 786 787 /* 788 * Find the method, which could be virtual or direct, defined directly 789 * or inherited. 790 */ 791 if (methodName[0] == '<') { 792 /* 793 * Constructor or class initializer. Only need to examine the 794 * "direct" list, and don't need to search up the class hierarchy. 795 */ 796 method = dvmFindDirectMethodByDescriptor(clazz, methodName, 797 methodDescr); 798 } else { 799 /* 800 * Try both lists, and scan up the tree. 801 */ 802 method = dvmFindVirtualMethodHierByDescriptor(clazz, methodName, 803 methodDescr); 804 if (method == NULL) { 805 method = dvmFindDirectMethodHierByDescriptor(clazz, methodName, 806 methodDescr); 807 } 808 } 809 810 if (method != NULL) { 811 /* 812 * Got it. See if there's a register map here. 813 */ 814 const RegisterMap* pMap; 815 pMap = dvmGetExpandedRegisterMap(method); 816 if (pMap == NULL) { 817 LOGV("No map for %s.%s %s\n", 818 classAndMethodDesc, methodName, methodDescr); 819 } else { 820 LOGV("Found map %s.%s %s\n", 821 classAndMethodDesc, methodName, methodDescr); 822 result = true; 823 } 824 } else { 825 LOGV("Unable to find %s.%s %s\n", 826 classAndMethodDesc, methodName, methodDescr); 827 } 828 829 bail: 830 free(classAndMethodDesc); 831 RETURN_BOOLEAN(result); 832 } 833 834 /* 835 * static void dumpReferenceTables() 836 */ 837 static void Dalvik_dalvik_system_VMDebug_dumpReferenceTables(const u4* args, 838 JValue* pResult) 839 { 840 UNUSED_PARAMETER(args); 841 UNUSED_PARAMETER(pResult); 842 843 LOGI("--- reference table dump ---\n"); 844 dvmDumpJniReferenceTables(); 845 // could dump thread's internalLocalRefTable, probably not useful 846 // ditto for thread's jniMonitorRefTable 847 LOGI("---\n"); 848 RETURN_VOID(); 849 } 850 851 /* 852 * static void crash() 853 * 854 * Dump the current thread's interpreted stack and abort the VM. Useful 855 * for seeing both interpreted and native stack traces. 856 * 857 * (Might want to restrict this to debuggable processes as a security 858 * measure, or check SecurityManager.checkExit().) 859 */ 860 static void Dalvik_dalvik_system_VMDebug_crash(const u4* args, 861 JValue* pResult) 862 { 863 UNUSED_PARAMETER(args); 864 UNUSED_PARAMETER(pResult); 865 866 LOGW("Crashing VM on request\n"); 867 dvmDumpThread(dvmThreadSelf(), false); 868 dvmAbort(); 869 } 870 871 /* 872 * static void infopoint(int id) 873 * 874 * Provide a hook for gdb to hang to so that the VM can be stopped when 875 * user-tagged source locations are being executed. 876 */ 877 static void Dalvik_dalvik_system_VMDebug_infopoint(const u4* args, 878 JValue* pResult) 879 { 880 gDvm.nativeDebuggerActive = true; 881 882 LOGD("VMDebug infopoint %d hit", args[0]); 883 884 gDvm.nativeDebuggerActive = false; 885 RETURN_VOID(); 886 } 887 888 static void Dalvik_dalvik_system_VMDebug_countInstancesOfClass(const u4* args, 889 JValue* pResult) 890 { 891 ClassObject* clazz = (ClassObject*)args[0]; 892 if (clazz == NULL) { 893 RETURN_LONG(0); 894 } else { 895 size_t count = dvmCountInstancesOfClass(clazz); 896 RETURN_LONG((long long)count); 897 } 898 } 899 900 const DalvikNativeMethod dvm_dalvik_system_VMDebug[] = { 901 { "getVmFeatureList", "()[Ljava/lang/String;", 902 Dalvik_dalvik_system_VMDebug_getVmFeatureList }, 903 { "getAllocCount", "(I)I", 904 Dalvik_dalvik_system_VMDebug_getAllocCount }, 905 { "resetAllocCount", "(I)V", 906 Dalvik_dalvik_system_VMDebug_resetAllocCount }, 907 { "startAllocCounting", "()V", 908 Dalvik_dalvik_system_VMDebug_startAllocCounting }, 909 { "stopAllocCounting", "()V", 910 Dalvik_dalvik_system_VMDebug_stopAllocCounting }, 911 { "startMethodTracingNative", "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V", 912 Dalvik_dalvik_system_VMDebug_startMethodTracingNative }, 913 { "isMethodTracingActive", "()Z", 914 Dalvik_dalvik_system_VMDebug_isMethodTracingActive }, 915 { "stopMethodTracing", "()V", 916 Dalvik_dalvik_system_VMDebug_stopMethodTracing }, 917 { "startEmulatorTracing", "()V", 918 Dalvik_dalvik_system_VMDebug_startEmulatorTracing }, 919 { "stopEmulatorTracing", "()V", 920 Dalvik_dalvik_system_VMDebug_stopEmulatorTracing }, 921 { "setAllocationLimit", "(I)I", 922 Dalvik_dalvik_system_VMDebug_setAllocationLimit }, 923 { "setGlobalAllocationLimit", "(I)I", 924 Dalvik_dalvik_system_VMDebug_setGlobalAllocationLimit }, 925 { "startInstructionCounting", "()V", 926 Dalvik_dalvik_system_VMDebug_startInstructionCounting }, 927 { "stopInstructionCounting", "()V", 928 Dalvik_dalvik_system_VMDebug_stopInstructionCounting }, 929 { "resetInstructionCount", "()V", 930 Dalvik_dalvik_system_VMDebug_resetInstructionCount }, 931 { "getInstructionCount", "([I)V", 932 Dalvik_dalvik_system_VMDebug_getInstructionCount }, 933 { "isDebuggerConnected", "()Z", 934 Dalvik_dalvik_system_VMDebug_isDebuggerConnected }, 935 { "isDebuggingEnabled", "()Z", 936 Dalvik_dalvik_system_VMDebug_isDebuggingEnabled }, 937 { "lastDebuggerActivity", "()J", 938 Dalvik_dalvik_system_VMDebug_lastDebuggerActivity }, 939 { "printLoadedClasses", "(I)V", 940 Dalvik_dalvik_system_VMDebug_printLoadedClasses }, 941 { "getLoadedClassCount", "()I", 942 Dalvik_dalvik_system_VMDebug_getLoadedClassCount }, 943 { "threadCpuTimeNanos", "()J", 944 Dalvik_dalvik_system_VMDebug_threadCpuTimeNanos }, 945 { "dumpHprofData", "(Ljava/lang/String;Ljava/io/FileDescriptor;)V", 946 Dalvik_dalvik_system_VMDebug_dumpHprofData }, 947 { "dumpHprofDataDdms", "()V", 948 Dalvik_dalvik_system_VMDebug_dumpHprofDataDdms }, 949 { "cacheRegisterMap", "(Ljava/lang/String;)Z", 950 Dalvik_dalvik_system_VMDebug_cacheRegisterMap }, 951 { "dumpReferenceTables", "()V", 952 Dalvik_dalvik_system_VMDebug_dumpReferenceTables }, 953 { "crash", "()V", 954 Dalvik_dalvik_system_VMDebug_crash }, 955 { "infopoint", "(I)V", 956 Dalvik_dalvik_system_VMDebug_infopoint }, 957 { "countInstancesOfClass", "(Ljava/lang/Class;)J", 958 Dalvik_dalvik_system_VMDebug_countInstancesOfClass }, 959 { NULL, NULL, NULL }, 960 }; 961