dalvik_system_VMDebug.cc revision ca620d7bc03b23a0bcf0ef58df58603ee000dca0
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#include "dalvik_system_VMDebug.h" 18 19#include <string.h> 20#include <unistd.h> 21 22#include <sstream> 23 24#include "base/histogram-inl.h" 25#include "base/time_utils.h" 26#include "class_linker.h" 27#include "common_throws.h" 28#include "debugger.h" 29#include "gc/space/bump_pointer_space.h" 30#include "gc/space/dlmalloc_space.h" 31#include "gc/space/large_object_space.h" 32#include "gc/space/space-inl.h" 33#include "gc/space/zygote_space.h" 34#include "hprof/hprof.h" 35#include "jni_internal.h" 36#include "mirror/class.h" 37#include "ScopedLocalRef.h" 38#include "ScopedUtfChars.h" 39#include "scoped_fast_native_object_access-inl.h" 40#include "trace.h" 41#include "well_known_classes.h" 42 43namespace art { 44 45static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) { 46 static const char* features[] = { 47 "method-trace-profiling", 48 "method-trace-profiling-streaming", 49 "method-sample-profiling", 50 "hprof-heap-dump", 51 "hprof-heap-dump-streaming", 52 }; 53 jobjectArray result = env->NewObjectArray(arraysize(features), 54 WellKnownClasses::java_lang_String, 55 nullptr); 56 if (result != nullptr) { 57 for (size_t i = 0; i < arraysize(features); ++i) { 58 ScopedLocalRef<jstring> jfeature(env, env->NewStringUTF(features[i])); 59 if (jfeature.get() == nullptr) { 60 return nullptr; 61 } 62 env->SetObjectArrayElement(result, i, jfeature.get()); 63 } 64 } 65 return result; 66} 67 68static void VMDebug_startAllocCounting(JNIEnv*, jclass) { 69 Runtime::Current()->SetStatsEnabled(true); 70} 71 72static void VMDebug_stopAllocCounting(JNIEnv*, jclass) { 73 Runtime::Current()->SetStatsEnabled(false); 74} 75 76static jint VMDebug_getAllocCount(JNIEnv*, jclass, jint kind) { 77 return Runtime::Current()->GetStat(kind); 78} 79 80static void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) { 81 Runtime::Current()->ResetStats(kinds); 82} 83 84static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags, 85 jboolean samplingEnabled, jint intervalUs) { 86 Trace::Start("[DDMS]", -1, bufferSize, flags, Trace::TraceOutputMode::kDDMS, 87 samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, 88 intervalUs); 89} 90 91static void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename, 92 jobject javaFd, jint bufferSize, jint flags, 93 jboolean samplingEnabled, jint intervalUs) { 94 int originalFd = jniGetFDFromFileDescriptor(env, javaFd); 95 if (originalFd < 0) { 96 return; 97 } 98 99 int fd = dup(originalFd); 100 if (fd < 0) { 101 ScopedObjectAccess soa(env); 102 soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", 103 "dup(%d) failed: %s", originalFd, strerror(errno)); 104 return; 105 } 106 107 ScopedUtfChars traceFilename(env, javaTraceFilename); 108 if (traceFilename.c_str() == nullptr) { 109 return; 110 } 111 Trace::Start(traceFilename.c_str(), fd, bufferSize, flags, Trace::TraceOutputMode::kFile, 112 samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, 113 intervalUs); 114} 115 116static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename, 117 jint bufferSize, jint flags, 118 jboolean samplingEnabled, jint intervalUs) { 119 ScopedUtfChars traceFilename(env, javaTraceFilename); 120 if (traceFilename.c_str() == nullptr) { 121 return; 122 } 123 Trace::Start(traceFilename.c_str(), -1, bufferSize, flags, Trace::TraceOutputMode::kFile, 124 samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing, 125 intervalUs); 126} 127 128static jint VMDebug_getMethodTracingMode(JNIEnv*, jclass) { 129 return Trace::GetMethodTracingMode(); 130} 131 132static void VMDebug_stopMethodTracing(JNIEnv*, jclass) { 133 Trace::Stop(); 134} 135 136static void VMDebug_startEmulatorTracing(JNIEnv*, jclass) { 137 UNIMPLEMENTED(WARNING); 138 // dvmEmulatorTraceStart(); 139} 140 141static void VMDebug_stopEmulatorTracing(JNIEnv*, jclass) { 142 UNIMPLEMENTED(WARNING); 143 // dvmEmulatorTraceStop(); 144} 145 146static jboolean VMDebug_isDebuggerConnected(JNIEnv*, jclass) { 147 return Dbg::IsDebuggerActive(); 148} 149 150static jboolean VMDebug_isDebuggingEnabled(JNIEnv*, jclass) { 151 return Dbg::IsJdwpConfigured(); 152} 153 154static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) { 155 return Dbg::LastDebuggerActivity(); 156} 157 158static void ThrowUnsupportedOperationException(JNIEnv* env) { 159 ScopedObjectAccess soa(env); 160 soa.Self()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", nullptr); 161} 162 163static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) { 164 ThrowUnsupportedOperationException(env); 165} 166 167static void VMDebug_stopInstructionCounting(JNIEnv* env, jclass) { 168 ThrowUnsupportedOperationException(env); 169} 170 171static void VMDebug_getInstructionCount(JNIEnv* env, jclass, jintArray /*javaCounts*/) { 172 ThrowUnsupportedOperationException(env); 173} 174 175static void VMDebug_resetInstructionCount(JNIEnv* env, jclass) { 176 ThrowUnsupportedOperationException(env); 177} 178 179static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) { 180 ScopedFastNativeObjectAccess soa(env); 181 return Runtime::Current()->GetClassLinker()->DumpAllClasses(flags); 182} 183 184static jint VMDebug_getLoadedClassCount(JNIEnv* env, jclass) { 185 ScopedFastNativeObjectAccess soa(env); 186 return Runtime::Current()->GetClassLinker()->NumLoadedClasses(); 187} 188 189/* 190 * Returns the thread-specific CPU-time clock value for the current thread, 191 * or -1 if the feature isn't supported. 192 */ 193static jlong VMDebug_threadCpuTimeNanos(JNIEnv*, jclass) { 194 return ThreadCpuNanoTime(); 195} 196 197/* 198 * static void dumpHprofData(String fileName, FileDescriptor fd) 199 * 200 * Cause "hprof" data to be dumped. We can throw an IOException if an 201 * error occurs during file handling. 202 */ 203static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jobject javaFd) { 204 // Only one of these may be null. 205 if (javaFilename == nullptr && javaFd == nullptr) { 206 ScopedObjectAccess soa(env); 207 ThrowNullPointerException("fileName == null && fd == null"); 208 return; 209 } 210 211 std::string filename; 212 if (javaFilename != nullptr) { 213 ScopedUtfChars chars(env, javaFilename); 214 if (env->ExceptionCheck()) { 215 return; 216 } 217 filename = chars.c_str(); 218 } else { 219 filename = "[fd]"; 220 } 221 222 int fd = -1; 223 if (javaFd != nullptr) { 224 fd = jniGetFDFromFileDescriptor(env, javaFd); 225 if (fd < 0) { 226 ScopedObjectAccess soa(env); 227 ThrowRuntimeException("Invalid file descriptor"); 228 return; 229 } 230 } 231 232 hprof::DumpHeap(filename.c_str(), fd, false); 233} 234 235static void VMDebug_dumpHprofDataDdms(JNIEnv*, jclass) { 236 hprof::DumpHeap("[DDMS]", -1, true); 237} 238 239static void VMDebug_dumpReferenceTables(JNIEnv* env, jclass) { 240 ScopedObjectAccess soa(env); 241 LOG(INFO) << "--- reference table dump ---"; 242 243 soa.Env()->DumpReferenceTables(LOG_STREAM(INFO)); 244 soa.Vm()->DumpReferenceTables(LOG_STREAM(INFO)); 245 246 LOG(INFO) << "---"; 247} 248 249static void VMDebug_crash(JNIEnv*, jclass) { 250 LOG(FATAL) << "Crashing runtime on request"; 251} 252 253static void VMDebug_infopoint(JNIEnv*, jclass, jint id) { 254 LOG(INFO) << "VMDebug infopoint " << id << " hit"; 255} 256 257static jlong VMDebug_countInstancesOfClass(JNIEnv* env, 258 jclass, 259 jclass javaClass, 260 jboolean countAssignable) { 261 ScopedObjectAccess soa(env); 262 gc::Heap* const heap = Runtime::Current()->GetHeap(); 263 // Caller's responsibility to do GC if desired. 264 ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(javaClass); 265 if (c == nullptr) { 266 return 0; 267 } 268 VariableSizedHandleScope hs(soa.Self()); 269 std::vector<Handle<mirror::Class>> classes {hs.NewHandle(c)}; 270 uint64_t count = 0; 271 heap->CountInstances(classes, countAssignable, &count); 272 return count; 273} 274 275static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, 276 jclass, 277 jobjectArray javaClasses, 278 jboolean countAssignable) { 279 ScopedObjectAccess soa(env); 280 gc::Heap* const heap = Runtime::Current()->GetHeap(); 281 // Caller's responsibility to do GC if desired. 282 ObjPtr<mirror::ObjectArray<mirror::Class>> decoded_classes = 283 soa.Decode<mirror::ObjectArray<mirror::Class>>(javaClasses); 284 if (decoded_classes == nullptr) { 285 return nullptr; 286 } 287 VariableSizedHandleScope hs(soa.Self()); 288 std::vector<Handle<mirror::Class>> classes; 289 for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) { 290 classes.push_back(hs.NewHandle(decoded_classes->Get(i))); 291 } 292 std::vector<uint64_t> counts(classes.size(), 0u); 293 // Heap::CountInstances can handle null and will put 0 for these classes. 294 heap->CountInstances(classes, countAssignable, &counts[0]); 295 ObjPtr<mirror::LongArray> long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size()); 296 if (long_counts == nullptr) { 297 soa.Self()->AssertPendingOOMException(); 298 return nullptr; 299 } 300 for (size_t i = 0; i < counts.size(); ++i) { 301 long_counts->Set(i, counts[i]); 302 } 303 return soa.AddLocalReference<jlongArray>(long_counts); 304} 305 306// We export the VM internal per-heap-space size/alloc/free metrics 307// for the zygote space, alloc space (application heap), and the large 308// object space for dumpsys meminfo. The other memory region data such 309// as PSS, private/shared dirty/shared data are available via 310// /proc/<pid>/smaps. 311static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) { 312 jlong* arr = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(data, 0)); 313 if (arr == nullptr || env->GetArrayLength(data) < 9) { 314 return; 315 } 316 317 size_t allocSize = 0; 318 size_t allocUsed = 0; 319 size_t zygoteSize = 0; 320 size_t zygoteUsed = 0; 321 size_t largeObjectsSize = 0; 322 size_t largeObjectsUsed = 0; 323 gc::Heap* heap = Runtime::Current()->GetHeap(); 324 { 325 ScopedObjectAccess soa(env); 326 for (gc::space::ContinuousSpace* space : heap->GetContinuousSpaces()) { 327 if (space->IsImageSpace()) { 328 // Currently don't include the image space. 329 } else if (space->IsZygoteSpace()) { 330 gc::space::ZygoteSpace* zygote_space = space->AsZygoteSpace(); 331 zygoteSize += zygote_space->Size(); 332 zygoteUsed += zygote_space->GetBytesAllocated(); 333 } else if (space->IsMallocSpace()) { 334 // This is a malloc space. 335 gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); 336 allocSize += malloc_space->GetFootprint(); 337 allocUsed += malloc_space->GetBytesAllocated(); 338 } else if (space->IsBumpPointerSpace()) { 339 gc::space::BumpPointerSpace* bump_pointer_space = space->AsBumpPointerSpace(); 340 allocSize += bump_pointer_space->Size(); 341 allocUsed += bump_pointer_space->GetBytesAllocated(); 342 } 343 } 344 for (gc::space::DiscontinuousSpace* space : heap->GetDiscontinuousSpaces()) { 345 if (space->IsLargeObjectSpace()) { 346 largeObjectsSize += space->AsLargeObjectSpace()->GetBytesAllocated(); 347 largeObjectsUsed += largeObjectsSize; 348 } 349 } 350 } 351 size_t allocFree = allocSize - allocUsed; 352 size_t zygoteFree = zygoteSize - zygoteUsed; 353 size_t largeObjectsFree = largeObjectsSize - largeObjectsUsed; 354 355 int j = 0; 356 arr[j++] = allocSize; 357 arr[j++] = allocUsed; 358 arr[j++] = allocFree; 359 arr[j++] = zygoteSize; 360 arr[j++] = zygoteUsed; 361 arr[j++] = zygoteFree; 362 arr[j++] = largeObjectsSize; 363 arr[j++] = largeObjectsUsed; 364 arr[j++] = largeObjectsFree; 365 env->ReleasePrimitiveArrayCritical(data, arr, 0); 366} 367 368// The runtime stat names for VMDebug.getRuntimeStat(). 369enum class VMDebugRuntimeStatId { 370 kArtGcGcCount = 0, 371 kArtGcGcTime, 372 kArtGcBytesAllocated, 373 kArtGcBytesFreed, 374 kArtGcBlockingGcCount, 375 kArtGcBlockingGcTime, 376 kArtGcGcCountRateHistogram, 377 kArtGcBlockingGcCountRateHistogram, 378 kNumRuntimeStats, 379}; 380 381static jobject VMDebug_getRuntimeStatInternal(JNIEnv* env, jclass, jint statId) { 382 gc::Heap* heap = Runtime::Current()->GetHeap(); 383 switch (static_cast<VMDebugRuntimeStatId>(statId)) { 384 case VMDebugRuntimeStatId::kArtGcGcCount: { 385 std::string output = std::to_string(heap->GetGcCount()); 386 return env->NewStringUTF(output.c_str()); 387 } 388 case VMDebugRuntimeStatId::kArtGcGcTime: { 389 std::string output = std::to_string(NsToMs(heap->GetGcTime())); 390 return env->NewStringUTF(output.c_str()); 391 } 392 case VMDebugRuntimeStatId::kArtGcBytesAllocated: { 393 std::string output = std::to_string(heap->GetBytesAllocatedEver()); 394 return env->NewStringUTF(output.c_str()); 395 } 396 case VMDebugRuntimeStatId::kArtGcBytesFreed: { 397 std::string output = std::to_string(heap->GetBytesFreedEver()); 398 return env->NewStringUTF(output.c_str()); 399 } 400 case VMDebugRuntimeStatId::kArtGcBlockingGcCount: { 401 std::string output = std::to_string(heap->GetBlockingGcCount()); 402 return env->NewStringUTF(output.c_str()); 403 } 404 case VMDebugRuntimeStatId::kArtGcBlockingGcTime: { 405 std::string output = std::to_string(NsToMs(heap->GetBlockingGcTime())); 406 return env->NewStringUTF(output.c_str()); 407 } 408 case VMDebugRuntimeStatId::kArtGcGcCountRateHistogram: { 409 std::ostringstream output; 410 heap->DumpGcCountRateHistogram(output); 411 return env->NewStringUTF(output.str().c_str()); 412 } 413 case VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram: { 414 std::ostringstream output; 415 heap->DumpBlockingGcCountRateHistogram(output); 416 return env->NewStringUTF(output.str().c_str()); 417 } 418 default: 419 return nullptr; 420 } 421} 422 423static bool SetRuntimeStatValue(JNIEnv* env, 424 jobjectArray result, 425 VMDebugRuntimeStatId id, 426 const std::string& value) { 427 ScopedLocalRef<jstring> jvalue(env, env->NewStringUTF(value.c_str())); 428 if (jvalue.get() == nullptr) { 429 return false; 430 } 431 env->SetObjectArrayElement(result, static_cast<jint>(id), jvalue.get()); 432 return true; 433} 434 435static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) { 436 jobjectArray result = env->NewObjectArray( 437 static_cast<jint>(VMDebugRuntimeStatId::kNumRuntimeStats), 438 WellKnownClasses::java_lang_String, 439 nullptr); 440 if (result == nullptr) { 441 return nullptr; 442 } 443 gc::Heap* heap = Runtime::Current()->GetHeap(); 444 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCount, 445 std::to_string(heap->GetGcCount()))) { 446 return nullptr; 447 } 448 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcTime, 449 std::to_string(NsToMs(heap->GetGcTime())))) { 450 return nullptr; 451 } 452 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesAllocated, 453 std::to_string(heap->GetBytesAllocatedEver()))) { 454 return nullptr; 455 } 456 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesFreed, 457 std::to_string(heap->GetBytesFreedEver()))) { 458 return nullptr; 459 } 460 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCount, 461 std::to_string(heap->GetBlockingGcCount()))) { 462 return nullptr; 463 } 464 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcTime, 465 std::to_string(NsToMs(heap->GetBlockingGcTime())))) { 466 return nullptr; 467 } 468 { 469 std::ostringstream output; 470 heap->DumpGcCountRateHistogram(output); 471 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCountRateHistogram, 472 output.str())) { 473 return nullptr; 474 } 475 } 476 { 477 std::ostringstream output; 478 heap->DumpBlockingGcCountRateHistogram(output); 479 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram, 480 output.str())) { 481 return nullptr; 482 } 483 } 484 return result; 485} 486 487static void VMDebug_attachAgent(JNIEnv* env, jclass, jstring agent) { 488 if (agent == nullptr) { 489 ScopedObjectAccess soa(env); 490 ThrowNullPointerException("agent is null"); 491 return; 492 } 493 494 if (!Dbg::IsJdwpAllowed()) { 495 ScopedObjectAccess soa(env); 496 ThrowSecurityException("Can't attach agent, process is not debuggable."); 497 return; 498 } 499 500 std::string filename; 501 { 502 ScopedUtfChars chars(env, agent); 503 if (env->ExceptionCheck()) { 504 return; 505 } 506 filename = chars.c_str(); 507 } 508 509 Runtime::Current()->AttachAgent(filename); 510} 511 512static JNINativeMethod gMethods[] = { 513 NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"), 514 NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"), 515 NATIVE_METHOD(VMDebug, crash, "()V"), 516 NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;Ljava/io/FileDescriptor;)V"), 517 NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"), 518 NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"), 519 NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"), 520 NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"), 521 NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"), 522 NATIVE_METHOD(VMDebug, getLoadedClassCount, "!()I"), 523 NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"), 524 NATIVE_METHOD(VMDebug, infopoint, "(I)V"), 525 NATIVE_METHOD(VMDebug, isDebuggerConnected, "!()Z"), 526 NATIVE_METHOD(VMDebug, isDebuggingEnabled, "!()Z"), 527 NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"), 528 NATIVE_METHOD(VMDebug, lastDebuggerActivity, "!()J"), 529 NATIVE_METHOD(VMDebug, printLoadedClasses, "!(I)V"), 530 NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"), 531 NATIVE_METHOD(VMDebug, resetInstructionCount, "()V"), 532 NATIVE_METHOD(VMDebug, startAllocCounting, "()V"), 533 NATIVE_METHOD(VMDebug, startEmulatorTracing, "()V"), 534 NATIVE_METHOD(VMDebug, startInstructionCounting, "()V"), 535 NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"), 536 NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;Ljava/io/FileDescriptor;IIZI)V"), 537 NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"), 538 NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"), 539 NATIVE_METHOD(VMDebug, stopEmulatorTracing, "()V"), 540 NATIVE_METHOD(VMDebug, stopInstructionCounting, "()V"), 541 NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"), 542 NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "!()J"), 543 NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"), 544 NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"), 545 NATIVE_METHOD(VMDebug, attachAgent, "(Ljava/lang/String;)V"), 546}; 547 548void register_dalvik_system_VMDebug(JNIEnv* env) { 549 REGISTER_NATIVE_METHODS("dalvik/system/VMDebug"); 550} 551 552} // namespace art 553