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