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.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(INFO)); 244 soa.Vm()->DumpReferenceTables(LOG(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, jclass, jclass javaClass, 258 jboolean countAssignable) { 259 ScopedObjectAccess soa(env); 260 gc::Heap* const heap = Runtime::Current()->GetHeap(); 261 // Caller's responsibility to do GC if desired. 262 mirror::Class* c = soa.Decode<mirror::Class*>(javaClass); 263 if (c == nullptr) { 264 return 0; 265 } 266 std::vector<mirror::Class*> classes {c}; 267 uint64_t count = 0; 268 heap->CountInstances(classes, countAssignable, &count); 269 return count; 270} 271 272static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, jclass, jobjectArray javaClasses, 273 jboolean countAssignable) { 274 ScopedObjectAccess soa(env); 275 gc::Heap* const heap = Runtime::Current()->GetHeap(); 276 // Caller's responsibility to do GC if desired. 277 auto* decoded_classes = soa.Decode<mirror::ObjectArray<mirror::Class>*>(javaClasses); 278 if (decoded_classes == nullptr) { 279 return nullptr; 280 } 281 std::vector<mirror::Class*> classes; 282 for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) { 283 classes.push_back(decoded_classes->Get(i)); 284 } 285 std::vector<uint64_t> counts(classes.size(), 0u); 286 // Heap::CountInstances can handle null and will put 0 for these classes. 287 heap->CountInstances(classes, countAssignable, &counts[0]); 288 auto* long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size()); 289 if (long_counts == nullptr) { 290 soa.Self()->AssertPendingOOMException(); 291 return nullptr; 292 } 293 for (size_t i = 0; i < counts.size(); ++i) { 294 long_counts->Set(i, counts[i]); 295 } 296 return soa.AddLocalReference<jlongArray>(long_counts); 297} 298 299// We export the VM internal per-heap-space size/alloc/free metrics 300// for the zygote space, alloc space (application heap), and the large 301// object space for dumpsys meminfo. The other memory region data such 302// as PSS, private/shared dirty/shared data are available via 303// /proc/<pid>/smaps. 304static void VMDebug_getHeapSpaceStats(JNIEnv* env, jclass, jlongArray data) { 305 jlong* arr = reinterpret_cast<jlong*>(env->GetPrimitiveArrayCritical(data, 0)); 306 if (arr == nullptr || env->GetArrayLength(data) < 9) { 307 return; 308 } 309 310 size_t allocSize = 0; 311 size_t allocUsed = 0; 312 size_t zygoteSize = 0; 313 size_t zygoteUsed = 0; 314 size_t largeObjectsSize = 0; 315 size_t largeObjectsUsed = 0; 316 gc::Heap* heap = Runtime::Current()->GetHeap(); 317 { 318 ScopedObjectAccess soa(env); 319 for (gc::space::ContinuousSpace* space : heap->GetContinuousSpaces()) { 320 if (space->IsImageSpace()) { 321 // Currently don't include the image space. 322 } else if (space->IsZygoteSpace()) { 323 gc::space::ZygoteSpace* zygote_space = space->AsZygoteSpace(); 324 zygoteSize += zygote_space->Size(); 325 zygoteUsed += zygote_space->GetBytesAllocated(); 326 } else if (space->IsMallocSpace()) { 327 // This is a malloc space. 328 gc::space::MallocSpace* malloc_space = space->AsMallocSpace(); 329 allocSize += malloc_space->GetFootprint(); 330 allocUsed += malloc_space->GetBytesAllocated(); 331 } else if (space->IsBumpPointerSpace()) { 332 gc::space::BumpPointerSpace* bump_pointer_space = space->AsBumpPointerSpace(); 333 allocSize += bump_pointer_space->Size(); 334 allocUsed += bump_pointer_space->GetBytesAllocated(); 335 } 336 } 337 for (gc::space::DiscontinuousSpace* space : heap->GetDiscontinuousSpaces()) { 338 if (space->IsLargeObjectSpace()) { 339 largeObjectsSize += space->AsLargeObjectSpace()->GetBytesAllocated(); 340 largeObjectsUsed += largeObjectsSize; 341 } 342 } 343 } 344 size_t allocFree = allocSize - allocUsed; 345 size_t zygoteFree = zygoteSize - zygoteUsed; 346 size_t largeObjectsFree = largeObjectsSize - largeObjectsUsed; 347 348 int j = 0; 349 arr[j++] = allocSize; 350 arr[j++] = allocUsed; 351 arr[j++] = allocFree; 352 arr[j++] = zygoteSize; 353 arr[j++] = zygoteUsed; 354 arr[j++] = zygoteFree; 355 arr[j++] = largeObjectsSize; 356 arr[j++] = largeObjectsUsed; 357 arr[j++] = largeObjectsFree; 358 env->ReleasePrimitiveArrayCritical(data, arr, 0); 359} 360 361// The runtime stat names for VMDebug.getRuntimeStat(). 362enum class VMDebugRuntimeStatId { 363 kArtGcGcCount = 0, 364 kArtGcGcTime, 365 kArtGcBytesAllocated, 366 kArtGcBytesFreed, 367 kArtGcBlockingGcCount, 368 kArtGcBlockingGcTime, 369 kArtGcGcCountRateHistogram, 370 kArtGcBlockingGcCountRateHistogram, 371 kNumRuntimeStats, 372}; 373 374static jobject VMDebug_getRuntimeStatInternal(JNIEnv* env, jclass, jint statId) { 375 gc::Heap* heap = Runtime::Current()->GetHeap(); 376 switch (static_cast<VMDebugRuntimeStatId>(statId)) { 377 case VMDebugRuntimeStatId::kArtGcGcCount: { 378 std::string output = std::to_string(heap->GetGcCount()); 379 return env->NewStringUTF(output.c_str()); 380 } 381 case VMDebugRuntimeStatId::kArtGcGcTime: { 382 std::string output = std::to_string(NsToMs(heap->GetGcTime())); 383 return env->NewStringUTF(output.c_str()); 384 } 385 case VMDebugRuntimeStatId::kArtGcBytesAllocated: { 386 std::string output = std::to_string(heap->GetBytesAllocatedEver()); 387 return env->NewStringUTF(output.c_str()); 388 } 389 case VMDebugRuntimeStatId::kArtGcBytesFreed: { 390 std::string output = std::to_string(heap->GetBytesFreedEver()); 391 return env->NewStringUTF(output.c_str()); 392 } 393 case VMDebugRuntimeStatId::kArtGcBlockingGcCount: { 394 std::string output = std::to_string(heap->GetBlockingGcCount()); 395 return env->NewStringUTF(output.c_str()); 396 } 397 case VMDebugRuntimeStatId::kArtGcBlockingGcTime: { 398 std::string output = std::to_string(NsToMs(heap->GetBlockingGcTime())); 399 return env->NewStringUTF(output.c_str()); 400 } 401 case VMDebugRuntimeStatId::kArtGcGcCountRateHistogram: { 402 std::ostringstream output; 403 heap->DumpGcCountRateHistogram(output); 404 return env->NewStringUTF(output.str().c_str()); 405 } 406 case VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram: { 407 std::ostringstream output; 408 heap->DumpBlockingGcCountRateHistogram(output); 409 return env->NewStringUTF(output.str().c_str()); 410 } 411 default: 412 return nullptr; 413 } 414} 415 416static bool SetRuntimeStatValue(JNIEnv* env, jobjectArray result, VMDebugRuntimeStatId id, 417 std::string value) { 418 ScopedLocalRef<jstring> jvalue(env, env->NewStringUTF(value.c_str())); 419 if (jvalue.get() == nullptr) { 420 return false; 421 } 422 env->SetObjectArrayElement(result, static_cast<jint>(id), jvalue.get()); 423 return true; 424} 425 426static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) { 427 jobjectArray result = env->NewObjectArray( 428 static_cast<jint>(VMDebugRuntimeStatId::kNumRuntimeStats), 429 WellKnownClasses::java_lang_String, 430 nullptr); 431 if (result == nullptr) { 432 return nullptr; 433 } 434 gc::Heap* heap = Runtime::Current()->GetHeap(); 435 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCount, 436 std::to_string(heap->GetGcCount()))) { 437 return nullptr; 438 } 439 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcTime, 440 std::to_string(NsToMs(heap->GetGcTime())))) { 441 return nullptr; 442 } 443 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesAllocated, 444 std::to_string(heap->GetBytesAllocatedEver()))) { 445 return nullptr; 446 } 447 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBytesFreed, 448 std::to_string(heap->GetBytesFreedEver()))) { 449 return nullptr; 450 } 451 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCount, 452 std::to_string(heap->GetBlockingGcCount()))) { 453 return nullptr; 454 } 455 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcTime, 456 std::to_string(NsToMs(heap->GetBlockingGcTime())))) { 457 return nullptr; 458 } 459 { 460 std::ostringstream output; 461 heap->DumpGcCountRateHistogram(output); 462 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcGcCountRateHistogram, 463 output.str())) { 464 return nullptr; 465 } 466 } 467 { 468 std::ostringstream output; 469 heap->DumpBlockingGcCountRateHistogram(output); 470 if (!SetRuntimeStatValue(env, result, VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram, 471 output.str())) { 472 return nullptr; 473 } 474 } 475 return result; 476} 477 478static JNINativeMethod gMethods[] = { 479 NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"), 480 NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"), 481 NATIVE_METHOD(VMDebug, crash, "()V"), 482 NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;Ljava/io/FileDescriptor;)V"), 483 NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"), 484 NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"), 485 NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"), 486 NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"), 487 NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"), 488 NATIVE_METHOD(VMDebug, getLoadedClassCount, "!()I"), 489 NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"), 490 NATIVE_METHOD(VMDebug, infopoint, "(I)V"), 491 NATIVE_METHOD(VMDebug, isDebuggerConnected, "!()Z"), 492 NATIVE_METHOD(VMDebug, isDebuggingEnabled, "!()Z"), 493 NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"), 494 NATIVE_METHOD(VMDebug, lastDebuggerActivity, "!()J"), 495 NATIVE_METHOD(VMDebug, printLoadedClasses, "!(I)V"), 496 NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"), 497 NATIVE_METHOD(VMDebug, resetInstructionCount, "()V"), 498 NATIVE_METHOD(VMDebug, startAllocCounting, "()V"), 499 NATIVE_METHOD(VMDebug, startEmulatorTracing, "()V"), 500 NATIVE_METHOD(VMDebug, startInstructionCounting, "()V"), 501 NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"), 502 NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;Ljava/io/FileDescriptor;IIZI)V"), 503 NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"), 504 NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"), 505 NATIVE_METHOD(VMDebug, stopEmulatorTracing, "()V"), 506 NATIVE_METHOD(VMDebug, stopInstructionCounting, "()V"), 507 NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"), 508 NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "!()J"), 509 NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"), 510 NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;") 511}; 512 513void register_dalvik_system_VMDebug(JNIEnv* env) { 514 REGISTER_NATIVE_METHODS("dalvik/system/VMDebug"); 515} 516 517} // namespace art 518