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 43 namespace art { 44 45 static 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 68 static void VMDebug_startAllocCounting(JNIEnv*, jclass) { 69 Runtime::Current()->SetStatsEnabled(true); 70 } 71 72 static void VMDebug_stopAllocCounting(JNIEnv*, jclass) { 73 Runtime::Current()->SetStatsEnabled(false); 74 } 75 76 static jint VMDebug_getAllocCount(JNIEnv*, jclass, jint kind) { 77 return Runtime::Current()->GetStat(kind); 78 } 79 80 static void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) { 81 Runtime::Current()->ResetStats(kinds); 82 } 83 84 static 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 91 static 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 116 static 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 128 static jint VMDebug_getMethodTracingMode(JNIEnv*, jclass) { 129 return Trace::GetMethodTracingMode(); 130 } 131 132 static void VMDebug_stopMethodTracing(JNIEnv*, jclass) { 133 Trace::Stop(); 134 } 135 136 static void VMDebug_startEmulatorTracing(JNIEnv*, jclass) { 137 UNIMPLEMENTED(WARNING); 138 // dvmEmulatorTraceStart(); 139 } 140 141 static void VMDebug_stopEmulatorTracing(JNIEnv*, jclass) { 142 UNIMPLEMENTED(WARNING); 143 // dvmEmulatorTraceStop(); 144 } 145 146 static jboolean VMDebug_isDebuggerConnected(JNIEnv*, jclass) { 147 return Dbg::IsDebuggerActive(); 148 } 149 150 static jboolean VMDebug_isDebuggingEnabled(JNIEnv*, jclass) { 151 return Dbg::IsJdwpConfigured(); 152 } 153 154 static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) { 155 return Dbg::LastDebuggerActivity(); 156 } 157 158 static void ThrowUnsupportedOperationException(JNIEnv* env) { 159 ScopedObjectAccess soa(env); 160 soa.Self()->ThrowNewException("Ljava/lang/UnsupportedOperationException;", nullptr); 161 } 162 163 static void VMDebug_startInstructionCounting(JNIEnv* env, jclass) { 164 ThrowUnsupportedOperationException(env); 165 } 166 167 static void VMDebug_stopInstructionCounting(JNIEnv* env, jclass) { 168 ThrowUnsupportedOperationException(env); 169 } 170 171 static void VMDebug_getInstructionCount(JNIEnv* env, jclass, jintArray /*javaCounts*/) { 172 ThrowUnsupportedOperationException(env); 173 } 174 175 static void VMDebug_resetInstructionCount(JNIEnv* env, jclass) { 176 ThrowUnsupportedOperationException(env); 177 } 178 179 static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) { 180 ScopedFastNativeObjectAccess soa(env); 181 return Runtime::Current()->GetClassLinker()->DumpAllClasses(flags); 182 } 183 184 static 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 */ 193 static 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 */ 203 static 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 235 static void VMDebug_dumpHprofDataDdms(JNIEnv*, jclass) { 236 hprof::DumpHeap("[DDMS]", -1, true); 237 } 238 239 static 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 249 static void VMDebug_crash(JNIEnv*, jclass) { 250 LOG(FATAL) << "Crashing runtime on request"; 251 } 252 253 static void VMDebug_infopoint(JNIEnv*, jclass, jint id) { 254 LOG(INFO) << "VMDebug infopoint " << id << " hit"; 255 } 256 257 static 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 272 static 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. 304 static 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(). 362 enum class VMDebugRuntimeStatId { 363 kArtGcGcCount = 0, 364 kArtGcGcTime, 365 kArtGcBytesAllocated, 366 kArtGcBytesFreed, 367 kArtGcBlockingGcCount, 368 kArtGcBlockingGcTime, 369 kArtGcGcCountRateHistogram, 370 kArtGcBlockingGcCountRateHistogram, 371 kNumRuntimeStats, 372 }; 373 374 static 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 416 static 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 426 static 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 478 static 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 513 void register_dalvik_system_VMDebug(JNIEnv* env) { 514 REGISTER_NATIVE_METHODS("dalvik/system/VMDebug"); 515 } 516 517 } // namespace art 518