xref: /aosp_15_r20/art/runtime/native/dalvik_system_VMDebug.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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/file_utils.h"
25 #include "base/histogram-inl.h"
26 #include "base/time_utils.h"
27 #include "class_linker.h"
28 #include "class_root-inl.h"
29 #include "common_throws.h"
30 #include "debugger.h"
31 #include "dex/class_accessor-inl.h"
32 #include "dex/descriptors_names.h"
33 #include "gc/space/bump_pointer_space.h"
34 #include "gc/space/dlmalloc_space.h"
35 #include "gc/space/large_object_space.h"
36 #include "gc/space/space-inl.h"
37 #include "gc/space/zygote_space.h"
38 #include "handle_scope-inl.h"
39 #include "hprof/hprof.h"
40 #include "jni/java_vm_ext.h"
41 #include "jni/jni_internal.h"
42 #include "mirror/array-alloc-inl.h"
43 #include "mirror/array-inl.h"
44 #include "mirror/class.h"
45 #include "mirror/executable-inl.h"
46 #include "mirror/object_array-alloc-inl.h"
47 #include "native_util.h"
48 #include "nativehelper/jni_macros.h"
49 #include "nativehelper/scoped_local_ref.h"
50 #include "nativehelper/scoped_utf_chars.h"
51 #include "nativehelper/utils.h"
52 #include "oat/oat_quick_method_header.h"
53 #include "scoped_fast_native_object_access-inl.h"
54 #include "string_array_utils.h"
55 #include "thread-inl.h"
56 #include "trace.h"
57 #include "trace_profile.h"
58 
59 namespace art HIDDEN {
60 
VMDebug_getVmFeatureList(JNIEnv * env,jclass)61 static jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) {
62   ScopedObjectAccess soa(Thread::ForEnv(env));
63   return soa.AddLocalReference<jobjectArray>(
64       CreateStringArray(soa.Self(),
65                         {
66                             "method-trace-profiling",
67                             "method-trace-profiling-streaming",
68                             "method-sample-profiling",
69                             "hprof-heap-dump",
70                             "hprof-heap-dump-streaming",
71                             "app_info",
72                         }));
73 }
74 
VMDebug_startAllocCounting(JNIEnv *,jclass)75 static void VMDebug_startAllocCounting(JNIEnv*, jclass) {
76   Runtime::Current()->SetStatsEnabled(true);
77 }
78 
VMDebug_stopAllocCounting(JNIEnv *,jclass)79 static void VMDebug_stopAllocCounting(JNIEnv*, jclass) {
80   Runtime::Current()->SetStatsEnabled(false);
81 }
82 
VMDebug_getAllocCount(JNIEnv *,jclass,jint kind)83 static jint VMDebug_getAllocCount(JNIEnv*, jclass, jint kind) {
84   return static_cast<jint>(Runtime::Current()->GetStat(kind));
85 }
86 
VMDebug_resetAllocCount(JNIEnv *,jclass,jint kinds)87 static void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) {
88   Runtime::Current()->ResetStats(kinds);
89 }
90 
VMDebug_startMethodTracingDdmsImpl(JNIEnv *,jclass,jint bufferSize,jint flags,jboolean samplingEnabled,jint intervalUs)91 static void VMDebug_startMethodTracingDdmsImpl(JNIEnv*, jclass, jint bufferSize, jint flags,
92                                                jboolean samplingEnabled, jint intervalUs) {
93   Trace::StartDDMS(bufferSize,
94                    flags,
95                    samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing,
96                    intervalUs);
97 }
98 
VMDebug_startMethodTracingFd(JNIEnv * env,jclass,jstring javaTraceFilename,jint javaFd,jint bufferSize,jint flags,jboolean samplingEnabled,jint intervalUs,jboolean streamingOutput)99 static void VMDebug_startMethodTracingFd(JNIEnv* env,
100                                          jclass,
101                                          [[maybe_unused]] jstring javaTraceFilename,
102                                          jint javaFd,
103                                          jint bufferSize,
104                                          jint flags,
105                                          jboolean samplingEnabled,
106                                          jint intervalUs,
107                                          jboolean streamingOutput) {
108   int originalFd = javaFd;
109   if (originalFd < 0) {
110     ScopedObjectAccess soa(env);
111     soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
112                                    "Trace fd is invalid: %d",
113                                    originalFd);
114     return;
115   }
116 
117   int fd = DupCloexec(originalFd);
118   if (fd < 0) {
119     ScopedObjectAccess soa(env);
120     soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
121                                    "dup(%d) failed: %s",
122                                    originalFd,
123                                    strerror(errno));
124     return;
125   }
126 
127   // Ignore the traceFilename.
128   TraceOutputMode outputMode =
129       streamingOutput ? TraceOutputMode::kStreaming : TraceOutputMode::kFile;
130   Trace::Start(fd,
131                bufferSize,
132                flags,
133                outputMode,
134                samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing,
135                intervalUs);
136 }
137 
VMDebug_startMethodTracingFilename(JNIEnv * env,jclass,jstring javaTraceFilename,jint bufferSize,jint flags,jboolean samplingEnabled,jint intervalUs)138 static void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename,
139                                                jint bufferSize, jint flags,
140                                                jboolean samplingEnabled, jint intervalUs) {
141   ScopedUtfChars traceFilename(env, javaTraceFilename);
142   if (traceFilename.c_str() == nullptr) {
143     return;
144   }
145   Trace::Start(traceFilename.c_str(),
146                bufferSize,
147                flags,
148                TraceOutputMode::kFile,
149                samplingEnabled ? Trace::TraceMode::kSampling : Trace::TraceMode::kMethodTracing,
150                intervalUs);
151 }
152 
VMDebug_getMethodTracingMode(JNIEnv *,jclass)153 static jint VMDebug_getMethodTracingMode(JNIEnv*, jclass) {
154   return Trace::GetMethodTracingMode();
155 }
156 
VMDebug_stopMethodTracing(JNIEnv *,jclass)157 static void VMDebug_stopMethodTracing(JNIEnv*, jclass) {
158   Trace::Stop();
159 }
160 
VMDebug_stopLowOverheadTraceImpl(JNIEnv *,jclass)161 static void VMDebug_stopLowOverheadTraceImpl(JNIEnv*, jclass) {
162   TraceProfiler::Stop();
163 }
164 
VMDebug_dumpLowOverheadTraceImpl(JNIEnv * env,jclass,jstring javaProfileFileName)165 static void VMDebug_dumpLowOverheadTraceImpl(JNIEnv* env, jclass, jstring javaProfileFileName) {
166   ScopedUtfChars profileFileName(env, javaProfileFileName);
167   if (profileFileName.c_str() == nullptr) {
168     LOG(ERROR) << "Filename not provided, ignoring the request to dump low-overhead trace";
169     return;
170   }
171   TraceProfiler::Dump(profileFileName.c_str());
172 }
173 
VMDebug_dumpLowOverheadTraceFdImpl(JNIEnv *,jclass,jint originalFd)174 static void VMDebug_dumpLowOverheadTraceFdImpl(JNIEnv*, jclass, jint originalFd) {
175   if (originalFd < 0) {
176     LOG(ERROR) << "Invalid file descriptor, ignoring the request to dump low-overhead trace";
177     return;
178   }
179 
180   // Set the O_CLOEXEC flag atomically here, so the file gets closed when a new process is forked.
181   int fd = DupCloexec(originalFd);
182   if (fd < 0) {
183     LOG(ERROR)
184         << "Unable to dup the file descriptor, ignoring the request to dump low-overhead trace";
185     return;
186   }
187 
188   TraceProfiler::Dump(fd);
189 }
190 
VMDebug_startLowOverheadTraceImpl(JNIEnv *,jclass)191 static void VMDebug_startLowOverheadTraceImpl(JNIEnv*, jclass) {
192   TraceProfiler::Start();
193 }
194 
VMDebug_isDebuggerConnected(JNIEnv *,jclass)195 static jboolean VMDebug_isDebuggerConnected(JNIEnv*, jclass) {
196   // This function will be replaced by the debugger when it's connected. See
197   // external/oj-libjdwp/src/share/vmDebug.c for implementation when debugger is connected.
198   return false;
199 }
200 
VMDebug_isDebuggingEnabled(JNIEnv * env,jclass)201 static jboolean VMDebug_isDebuggingEnabled(JNIEnv* env, jclass) {
202   ScopedObjectAccess soa(env);
203   return Runtime::Current()->GetRuntimeCallbacks()->IsDebuggerConfigured();
204 }
205 
VMDebug_lastDebuggerActivity(JNIEnv *,jclass)206 static jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) {
207   // This function will be replaced by the debugger when it's connected. See
208   // external/oj-libjdwp/src/share/vmDebug.c for implementation when debugger is connected.
209   return -1;
210 }
211 
VMDebug_suspendAllAndSendVmStart(JNIEnv *,jclass)212 static void VMDebug_suspendAllAndSendVmStart(JNIEnv*, jclass)
213     REQUIRES_SHARED(Locks::mutator_lock_) {
214   // This function will be replaced by the debugger when it's connected. See
215   // external/oj-libjdwp/src/share/vmDebug.c for implementation when debugger is connected.
216   ThrowRuntimeException("ART's suspendAllAndSendVmStart is not implemented");
217 }
218 
VMDebug_printLoadedClasses(JNIEnv * env,jclass,jint flags)219 static void VMDebug_printLoadedClasses(JNIEnv* env, jclass, jint flags) {
220   class DumpClassVisitor : public ClassVisitor {
221    public:
222     explicit DumpClassVisitor(int dump_flags) : flags_(dump_flags) {}
223 
224     bool operator()(ObjPtr<mirror::Class> klass) override REQUIRES_SHARED(Locks::mutator_lock_) {
225       klass->DumpClass(LOG_STREAM(ERROR), flags_);
226       return true;
227     }
228 
229    private:
230     const int flags_;
231   };
232   DumpClassVisitor visitor(flags);
233 
234   ScopedFastNativeObjectAccess soa(env);
235   return Runtime::Current()->GetClassLinker()->VisitClasses(&visitor);
236 }
237 
VMDebug_getLoadedClassCount(JNIEnv * env,jclass)238 static jint VMDebug_getLoadedClassCount(JNIEnv* env, jclass) {
239   ScopedFastNativeObjectAccess soa(env);
240   return Runtime::Current()->GetClassLinker()->NumLoadedClasses();
241 }
242 
243 /*
244  * Returns the thread-specific CPU-time clock value for the current thread,
245  * or -1 if the feature isn't supported.
246  */
VMDebug_threadCpuTimeNanos(JNIEnv *,jclass)247 static jlong VMDebug_threadCpuTimeNanos(JNIEnv*, jclass) {
248   return ThreadCpuNanoTime();
249 }
250 
251 /*
252  * static void dumpHprofData(String fileName, FileDescriptor fd)
253  *
254  * Cause "hprof" data to be dumped.  We can throw an IOException if an
255  * error occurs during file handling.
256  */
VMDebug_dumpHprofData(JNIEnv * env,jclass,jstring javaFilename,jint javaFd)257 static void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jint javaFd) {
258   // Only one of these may be null.
259   if (javaFilename == nullptr && javaFd < 0) {
260     ScopedObjectAccess soa(env);
261     ThrowNullPointerException("fileName == null && fd == null");
262     return;
263   }
264 
265   std::string filename;
266   if (javaFilename != nullptr) {
267     ScopedUtfChars chars(env, javaFilename);
268     if (env->ExceptionCheck()) {
269       return;
270     }
271     filename = chars.c_str();
272   } else {
273     filename = "[fd]";
274   }
275 
276   int fd = javaFd;
277 
278   hprof::DumpHeap(filename.c_str(), fd, false);
279 }
280 
VMDebug_dumpHprofDataDdms(JNIEnv *,jclass)281 static void VMDebug_dumpHprofDataDdms(JNIEnv*, jclass) {
282   hprof::DumpHeap("[DDMS]", -1, true);
283 }
284 
VMDebug_dumpReferenceTables(JNIEnv * env,jclass)285 static void VMDebug_dumpReferenceTables(JNIEnv* env, jclass) {
286   ScopedObjectAccess soa(env);
287   LOG(INFO) << "--- reference table dump ---";
288 
289   soa.Env()->DumpReferenceTables(LOG_STREAM(INFO));
290   soa.Vm()->DumpReferenceTables(LOG_STREAM(INFO));
291 
292   LOG(INFO) << "---";
293 }
294 
VMDebug_countInstancesOfClass(JNIEnv * env,jclass,jclass javaClass,jboolean countAssignable)295 static jlong VMDebug_countInstancesOfClass(JNIEnv* env,
296                                            jclass,
297                                            jclass javaClass,
298                                            jboolean countAssignable) {
299   ScopedObjectAccess soa(env);
300   gc::Heap* const heap = Runtime::Current()->GetHeap();
301   // Caller's responsibility to do GC if desired.
302   ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(javaClass);
303   if (c == nullptr) {
304     return 0;
305   }
306   VariableSizedHandleScope hs(soa.Self());
307   std::vector<Handle<mirror::Class>> classes {hs.NewHandle(c)};
308   uint64_t count = 0;
309   heap->CountInstances(classes, countAssignable, &count);
310   return count;
311 }
312 
VMDebug_getExecutableMethodFileOffsetsNative(JNIEnv * env,jclass,jobject javaMethod)313 static jobject VMDebug_getExecutableMethodFileOffsetsNative(JNIEnv* env,
314                                                             jclass,
315                                                             jobject javaMethod) {
316   ScopedObjectAccess soa(env);
317   ObjPtr<mirror::Executable> m = soa.Decode<mirror::Executable>(javaMethod);
318   if (m == nullptr) {
319     soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
320                                    "Could not find mirror::Executable for supplied jobject");
321     return nullptr;
322   }
323 
324   ObjPtr<mirror::Class> c = m->GetDeclaringClass();
325   if (c == nullptr) {
326     soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
327                                    "Could not find mirror::Class for supplied jobject");
328     return nullptr;
329   }
330 
331   ArtMethod* art_method = m->GetArtMethod();
332   auto oat_method_quick_code =
333       reinterpret_cast<const uint8_t*>(art_method->GetOatMethodQuickCode(kRuntimePointerSize));
334 
335   if (oat_method_quick_code == nullptr) {
336     LOG(ERROR) << "No OatMethodQuickCode for method " << art_method->PrettyMethod();
337     return nullptr;
338   }
339 
340   const OatDexFile* oat_dex_file = c->GetDexFile().GetOatDexFile();
341   if (oat_dex_file == nullptr) {
342     soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Could not find oat_dex_file");
343     return nullptr;
344   }
345 
346   const OatFile* oat_file = oat_dex_file->GetOatFile();
347   if (oat_file == nullptr) {
348     soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;", "Could not find oat_file");
349     return nullptr;
350   }
351 
352   std::string error_msg;
353   const uint8_t* elf_begin = oat_file->ComputeElfBegin(&error_msg);
354   if (elf_begin == nullptr) {
355     soa.Self()->ThrowNewExceptionF(
356         "Ljava/lang/RuntimeException;", "Could not find elf_begin: %s", error_msg.c_str());
357     return nullptr;
358   }
359 
360   size_t adjusted_offset = oat_method_quick_code - elf_begin;
361 
362   ScopedLocalRef<jstring> odex_path = CREATE_UTF_OR_RETURN(env, oat_file->GetLocation());
363   auto odex_offset = reinterpret_cast64<jlong>(elf_begin);
364   auto method_offset = static_cast<jlong>(adjusted_offset);
365 
366   ScopedLocalRef<jclass> clazz(env,
367                                env->FindClass("dalvik/system/VMDebug$ExecutableMethodFileOffsets"));
368   if (clazz == nullptr) {
369     soa.Self()->ThrowNewExceptionF(
370         "Ljava/lang/RuntimeException;",
371         "Could not find dalvik/system/VMDebug$ExecutableMethodFileOffsets");
372     return nullptr;
373   }
374 
375   jmethodID constructor_id = env->GetMethodID(clazz.get(), "<init>", "(Ljava/lang/String;JJ)V");
376   return env->NewObject(clazz.get(), constructor_id, odex_path.get(), odex_offset, method_offset);
377 }
378 
VMDebug_countInstancesOfClasses(JNIEnv * env,jclass,jobjectArray javaClasses,jboolean countAssignable)379 static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env,
380                                                   jclass,
381                                                   jobjectArray javaClasses,
382                                                   jboolean countAssignable) {
383   ScopedObjectAccess soa(env);
384   gc::Heap* const heap = Runtime::Current()->GetHeap();
385   // Caller's responsibility to do GC if desired.
386   ObjPtr<mirror::ObjectArray<mirror::Class>> decoded_classes =
387       soa.Decode<mirror::ObjectArray<mirror::Class>>(javaClasses);
388   if (decoded_classes == nullptr) {
389     return nullptr;
390   }
391   VariableSizedHandleScope hs(soa.Self());
392   std::vector<Handle<mirror::Class>> classes;
393   for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) {
394     classes.push_back(hs.NewHandle(decoded_classes->Get(i)));
395   }
396   std::vector<uint64_t> counts(classes.size(), 0u);
397   // Heap::CountInstances can handle null and will put 0 for these classes.
398   heap->CountInstances(classes, countAssignable, &counts[0]);
399   ObjPtr<mirror::LongArray> long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size());
400   if (long_counts == nullptr) {
401     soa.Self()->AssertPendingOOMException();
402     return nullptr;
403   }
404   for (size_t i = 0; i < counts.size(); ++i) {
405     long_counts->Set(i, counts[i]);
406   }
407   return soa.AddLocalReference<jlongArray>(long_counts);
408 }
409 
410 // The runtime stat names for VMDebug.getRuntimeStat().
411 enum class VMDebugRuntimeStatId {
412   kArtGcGcCount = 0,
413   kArtGcGcTime,
414   kArtGcBytesAllocated,
415   kArtGcBytesFreed,
416   kArtGcBlockingGcCount,
417   kArtGcBlockingGcTime,
418   kArtGcGcCountRateHistogram,
419   kArtGcBlockingGcCountRateHistogram,
420   kArtGcObjectsAllocated,
421   kArtGcTotalTimeWaitingForGc,
422   kArtGcPreOomeGcCount,
423   kNumRuntimeStats,
424 };
425 
VMDebug_getRuntimeStatInternal(JNIEnv * env,jclass,jint statId)426 static jstring VMDebug_getRuntimeStatInternal(JNIEnv* env, jclass, jint statId) {
427   gc::Heap* heap = Runtime::Current()->GetHeap();
428   switch (static_cast<VMDebugRuntimeStatId>(statId)) {
429     case VMDebugRuntimeStatId::kArtGcGcCount: {
430       std::string output = std::to_string(heap->GetGcCount());
431       return env->NewStringUTF(output.c_str());
432     }
433     case VMDebugRuntimeStatId::kArtGcGcTime: {
434       std::string output = std::to_string(NsToMs(heap->GetGcTime()));
435       return env->NewStringUTF(output.c_str());
436     }
437     case VMDebugRuntimeStatId::kArtGcBytesAllocated: {
438       std::string output = std::to_string(heap->GetBytesAllocatedEver());
439       return env->NewStringUTF(output.c_str());
440     }
441     case VMDebugRuntimeStatId::kArtGcBytesFreed: {
442       std::string output = std::to_string(heap->GetBytesFreedEver());
443       return env->NewStringUTF(output.c_str());
444     }
445     case VMDebugRuntimeStatId::kArtGcBlockingGcCount: {
446       std::string output = std::to_string(heap->GetBlockingGcCount());
447       return env->NewStringUTF(output.c_str());
448     }
449     case VMDebugRuntimeStatId::kArtGcBlockingGcTime: {
450       std::string output = std::to_string(NsToMs(heap->GetBlockingGcTime()));
451       return env->NewStringUTF(output.c_str());
452     }
453     case VMDebugRuntimeStatId::kArtGcGcCountRateHistogram: {
454       std::ostringstream output;
455       heap->DumpGcCountRateHistogram(output);
456       return env->NewStringUTF(output.str().c_str());
457     }
458     case VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram: {
459       std::ostringstream output;
460       heap->DumpBlockingGcCountRateHistogram(output);
461       return env->NewStringUTF(output.str().c_str());
462     }
463     case VMDebugRuntimeStatId::kArtGcObjectsAllocated: {
464       std::string output = std::to_string(heap->GetObjectsAllocated());
465       return env->NewStringUTF(output.c_str());
466     }
467     case VMDebugRuntimeStatId::kArtGcTotalTimeWaitingForGc: {
468       std::string output = std::to_string(heap->GetTotalTimeWaitingForGC());
469       return env->NewStringUTF(output.c_str());
470     }
471     case VMDebugRuntimeStatId::kArtGcPreOomeGcCount: {
472       std::string output = std::to_string(heap->GetPreOomeGcCount());
473       return env->NewStringUTF(output.c_str());
474     }
475     default:
476       return nullptr;
477   }
478 }
479 
SetRuntimeStatValue(Thread * self,Handle<mirror::ObjectArray<mirror::String>> array,VMDebugRuntimeStatId id,const std::string & value)480 static bool SetRuntimeStatValue(Thread* self,
481                                 Handle<mirror::ObjectArray<mirror::String>> array,
482                                 VMDebugRuntimeStatId id,
483                                 const std::string& value) REQUIRES_SHARED(Locks::mutator_lock_) {
484   ObjPtr<mirror::String> ovalue = mirror::String::AllocFromModifiedUtf8(self, value.c_str());
485   if (ovalue == nullptr) {
486     DCHECK(self->IsExceptionPending());
487     return false;
488   }
489   // We're initializing a newly allocated array object, so we do not need to record that under
490   // a transaction. If the transaction is aborted, the whole object shall be unreachable.
491   array->SetWithoutChecks</*kTransactionActive=*/ false, /*kCheckTransaction=*/ false>(
492       static_cast<int32_t>(id), ovalue);
493   return true;
494 }
495 
VMDebug_getRuntimeStatsInternal(JNIEnv * env,jclass)496 static jobjectArray VMDebug_getRuntimeStatsInternal(JNIEnv* env, jclass) {
497   Thread* self = Thread::ForEnv(env);
498   ScopedObjectAccess soa(self);
499   StackHandleScope<1u> hs(self);
500   int32_t size = enum_cast<int32_t>(VMDebugRuntimeStatId::kNumRuntimeStats);
501   Handle<mirror::ObjectArray<mirror::String>> array = hs.NewHandle(
502       mirror::ObjectArray<mirror::String>::Alloc(
503           self, GetClassRoot<mirror::ObjectArray<mirror::String>>(), size));
504   if (array == nullptr) {
505     DCHECK(self->IsExceptionPending());
506     return nullptr;
507   }
508   gc::Heap* heap = Runtime::Current()->GetHeap();
509   if (!SetRuntimeStatValue(self,
510                            array,
511                            VMDebugRuntimeStatId::kArtGcGcCount,
512                            std::to_string(heap->GetGcCount()))) {
513     return nullptr;
514   }
515   if (!SetRuntimeStatValue(self,
516                            array,
517                            VMDebugRuntimeStatId::kArtGcGcTime,
518                            std::to_string(NsToMs(heap->GetGcTime())))) {
519     return nullptr;
520   }
521   if (!SetRuntimeStatValue(self,
522                            array,
523                            VMDebugRuntimeStatId::kArtGcBytesAllocated,
524                            std::to_string(heap->GetBytesAllocatedEver()))) {
525     return nullptr;
526   }
527   if (!SetRuntimeStatValue(self,
528                            array,
529                            VMDebugRuntimeStatId::kArtGcBytesFreed,
530                            std::to_string(heap->GetBytesFreedEver()))) {
531     return nullptr;
532   }
533   if (!SetRuntimeStatValue(self,
534                            array,
535                            VMDebugRuntimeStatId::kArtGcBlockingGcCount,
536                            std::to_string(heap->GetBlockingGcCount()))) {
537     return nullptr;
538   }
539   if (!SetRuntimeStatValue(self,
540                            array,
541                            VMDebugRuntimeStatId::kArtGcBlockingGcTime,
542                            std::to_string(NsToMs(heap->GetBlockingGcTime())))) {
543     return nullptr;
544   }
545   {
546     std::ostringstream output;
547     heap->DumpGcCountRateHistogram(output);
548     if (!SetRuntimeStatValue(self,
549                              array,
550                              VMDebugRuntimeStatId::kArtGcGcCountRateHistogram,
551                              output.str())) {
552       return nullptr;
553     }
554   }
555   {
556     std::ostringstream output;
557     heap->DumpBlockingGcCountRateHistogram(output);
558     if (!SetRuntimeStatValue(self,
559                              array,
560                              VMDebugRuntimeStatId::kArtGcBlockingGcCountRateHistogram,
561                              output.str())) {
562       return nullptr;
563     }
564   }
565   return soa.AddLocalReference<jobjectArray>(array.Get());
566 }
567 
VMDebug_nativeAttachAgent(JNIEnv * env,jclass,jstring agent,jobject classloader)568 static void VMDebug_nativeAttachAgent(JNIEnv* env, jclass, jstring agent, jobject classloader) {
569   if (agent == nullptr) {
570     ScopedObjectAccess soa(env);
571     ThrowNullPointerException("agent is null");
572     return;
573   }
574 
575   if (!Dbg::IsJdwpAllowed()) {
576     ScopedObjectAccess soa(env);
577     ThrowSecurityException("Can't attach agent, process is not debuggable.");
578     return;
579   }
580 
581   std::string filename;
582   {
583     ScopedUtfChars chars(env, agent);
584     if (env->ExceptionCheck()) {
585       return;
586     }
587     filename = chars.c_str();
588   }
589 
590   Runtime::Current()->AttachAgent(env, filename, classloader);
591 }
592 
VMDebug_allowHiddenApiReflectionFrom(JNIEnv * env,jclass,jclass j_caller)593 static void VMDebug_allowHiddenApiReflectionFrom(JNIEnv* env, jclass, jclass j_caller) {
594   Runtime* runtime = Runtime::Current();
595   ScopedObjectAccess soa(env);
596 
597   if (!runtime->IsJavaDebuggableAtInit()) {
598     ThrowSecurityException("Can't exempt class, process is not debuggable.");
599     return;
600   }
601 
602   StackHandleScope<1> hs(soa.Self());
603   Handle<mirror::Class> h_caller(hs.NewHandle(soa.Decode<mirror::Class>(j_caller)));
604   if (h_caller.IsNull()) {
605     ThrowNullPointerException("argument is null");
606     return;
607   }
608 
609   h_caller->SetSkipHiddenApiChecks();
610 }
611 
VMDebug_setAllocTrackerStackDepth(JNIEnv * env,jclass,jint stack_depth)612 static void VMDebug_setAllocTrackerStackDepth(JNIEnv* env, jclass, jint stack_depth) {
613   Runtime* runtime = Runtime::Current();
614   if (stack_depth < 0 ||
615       static_cast<size_t>(stack_depth) > gc::AllocRecordObjectMap::kMaxSupportedStackDepth) {
616     ScopedObjectAccess soa(env);
617     soa.Self()->ThrowNewExceptionF("Ljava/lang/RuntimeException;",
618                                    "Stack depth is invalid: %d",
619                                    stack_depth);
620   } else {
621     runtime->GetHeap()->SetAllocTrackerStackDepth(static_cast<size_t>(stack_depth));
622   }
623 }
624 
VMDebug_setCurrentProcessName(JNIEnv * env,jclass,jstring process_name)625 static void VMDebug_setCurrentProcessName(JNIEnv* env, jclass, jstring process_name) {
626   ScopedObjectAccess soa(env);
627 
628   // Android application ID naming convention states:
629   // "The name can contain uppercase or lowercase letters, numbers, and underscores ('_')"
630   // This is fine to convert to std::string
631   const char* c_process_name = env->GetStringUTFChars(process_name, NULL);
632   Runtime::Current()->GetRuntimeCallbacks()->SetCurrentProcessName(std::string(c_process_name));
633   env->ReleaseStringUTFChars(process_name, c_process_name);
634 }
635 
VMDebug_addApplication(JNIEnv * env,jclass,jstring package_name)636 static void VMDebug_addApplication(JNIEnv* env, jclass, jstring package_name) {
637   ScopedObjectAccess soa(env);
638 
639   // Android application ID naming convention states:
640   // "The name can contain uppercase or lowercase letters, numbers, and underscores ('_')"
641   // This is fine to convert to std::string
642   const char* c_package_name = env->GetStringUTFChars(package_name, NULL);
643   Runtime::Current()->GetRuntimeCallbacks()->AddApplication(std::string(c_package_name));
644   env->ReleaseStringUTFChars(package_name, c_package_name);
645 }
646 
VMDebug_removeApplication(JNIEnv * env,jclass,jstring package_name)647 static void VMDebug_removeApplication(JNIEnv* env, jclass, jstring package_name) {
648   ScopedObjectAccess soa(env);
649 
650   // Android application ID naming convention states:
651   // "The name can contain uppercase or lowercase letters, numbers, and underscores ('_')"
652   // This is fine to convert to std::string
653   const char* c_package_name = env->GetStringUTFChars(package_name, NULL);
654   Runtime::Current()->GetRuntimeCallbacks()->RemoveApplication(std::string(c_package_name));
655   env->ReleaseStringUTFChars(package_name, c_package_name);
656 }
657 
VMDebug_setWaitingForDebugger(JNIEnv * env,jclass,jboolean waiting)658 static void VMDebug_setWaitingForDebugger(JNIEnv* env, jclass, jboolean waiting) {
659   ScopedObjectAccess soa(env);
660   Runtime::Current()->GetRuntimeCallbacks()->SetWaitingForDebugger(waiting);
661 }
662 
VMDebug_setUserId(JNIEnv * env,jclass,jint user_id)663 static void VMDebug_setUserId(JNIEnv* env, jclass, jint user_id) {
664   ScopedObjectAccess soa(env);
665   Runtime::Current()->GetRuntimeCallbacks()->SetUserId(user_id);
666 }
667 
668 static JNINativeMethod gMethods[] = {
669     NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
670     NATIVE_METHOD(VMDebug, countInstancesOfClasses, "([Ljava/lang/Class;Z)[J"),
671     NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;I)V"),
672     NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
673     NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"),
674     NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
675     FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"),
676     NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"),
677     FAST_NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"),
678     FAST_NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"),
679     NATIVE_METHOD(VMDebug, suspendAllAndSendVmStart, "()V"),
680     NATIVE_METHOD(VMDebug, getMethodTracingMode, "()I"),
681     FAST_NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"),
682     FAST_NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"),
683     NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"),
684     NATIVE_METHOD(VMDebug, startAllocCounting, "()V"),
685     NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(IIZI)V"),
686     NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;IIIZIZ)V"),
687     NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;IIZI)V"),
688     NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"),
689     NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"),
690     FAST_NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"),
691     NATIVE_METHOD(VMDebug, getRuntimeStatInternal, "(I)Ljava/lang/String;"),
692     NATIVE_METHOD(VMDebug, getRuntimeStatsInternal, "()[Ljava/lang/String;"),
693     NATIVE_METHOD(VMDebug, nativeAttachAgent, "(Ljava/lang/String;Ljava/lang/ClassLoader;)V"),
694     NATIVE_METHOD(VMDebug, allowHiddenApiReflectionFrom, "(Ljava/lang/Class;)V"),
695     NATIVE_METHOD(VMDebug, setAllocTrackerStackDepth, "(I)V"),
696     NATIVE_METHOD(VMDebug, setCurrentProcessName, "(Ljava/lang/String;)V"),
697     NATIVE_METHOD(VMDebug, setWaitingForDebugger, "(Z)V"),
698     NATIVE_METHOD(VMDebug, addApplication, "(Ljava/lang/String;)V"),
699     NATIVE_METHOD(VMDebug, removeApplication, "(Ljava/lang/String;)V"),
700     NATIVE_METHOD(VMDebug, setUserId, "(I)V"),
701     NATIVE_METHOD(VMDebug, startLowOverheadTraceImpl, "()V"),
702     NATIVE_METHOD(VMDebug, stopLowOverheadTraceImpl, "()V"),
703     NATIVE_METHOD(VMDebug, dumpLowOverheadTraceImpl, "(Ljava/lang/String;)V"),
704     NATIVE_METHOD(VMDebug, dumpLowOverheadTraceFdImpl, "(I)V"),
705     NATIVE_METHOD(
706         VMDebug,
707         getExecutableMethodFileOffsetsNative,
708         "(Ljava/lang/reflect/Method;)Ldalvik/system/VMDebug$ExecutableMethodFileOffsets;"),
709 };
710 
register_dalvik_system_VMDebug(JNIEnv * env)711 void register_dalvik_system_VMDebug(JNIEnv* env) {
712   REGISTER_NATIVE_METHODS("dalvik/system/VMDebug");
713 }
714 
715 }  // namespace art
716