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