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