Implement as much of VMDebug as we can reasonably do right now.
No hprof and no method tracing, but everything else.
Change-Id: Ifccd1f08e31f34b947c30f1211db788aae674d81
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 94c7b15..0f12340 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -99,6 +99,7 @@
src/image_writer.cc \
src/indirect_reference_table.cc \
src/intern_table.cc \
+ src/dalvik_system_VMDebug.cc \
src/java_lang_Class.cc \
src/java_lang_Object.cc \
src/java_lang_Runtime.cc \
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 6832b84..5e05439 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -1297,6 +1297,9 @@
if (self->IsExceptionPending()) {
klass->SetStatus(Class::kStatusError);
} else {
+ ++Runtime::Current()->GetStats()->class_init_count;
+ ++self->GetStats()->class_init_count;
+ // TODO: class_init_time_ns
klass->SetStatus(Class::kStatusInitialized);
}
lock.NotifyAll();
@@ -2155,6 +2158,23 @@
return resolved;
}
+void ClassLinker::DumpAllClasses(int flags) const {
+ // TODO: at the time this was written, it wasn't safe to call PrettyField with the ClassLinker
+ // lock held, because it might need to resolve a field's type, which would try to take the lock.
+ std::vector<Class*> all_classes;
+ {
+ MutexLock mu(lock_);
+ typedef Table::const_iterator It; // TODO: C++0x auto
+ for (It it = classes_.begin(), end = classes_.end(); it != end; ++it) {
+ all_classes.push_back(it->second);
+ }
+ }
+
+ for (size_t i = 0; i < all_classes.size(); ++i) {
+ all_classes[i]->DumpClass(std::cerr, flags);
+ }
+}
+
size_t ClassLinker::NumLoadedClasses() const {
MutexLock mu(lock_);
return classes_.size();
diff --git a/src/class_linker.h b/src/class_linker.h
index 18bd9d6..ca71b1f 100644
--- a/src/class_linker.h
+++ b/src/class_linker.h
@@ -42,6 +42,8 @@
return FindClass(descriptor, NULL);
}
+ void DumpAllClasses(int flags) const;
+
size_t NumLoadedClasses() const;
// Resolve a String with the given index from the DexFile, storing the
diff --git a/src/dalvik_system_VMDebug.cc b/src/dalvik_system_VMDebug.cc
new file mode 100644
index 0000000..1defb18
--- /dev/null
+++ b/src/dalvik_system_VMDebug.cc
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "class_linker.h"
+#include "jni_internal.h"
+#include "ScopedUtfChars.h"
+#include "toStringArray.h"
+
+#include "JniConstants.h" // Last to avoid problems with LOG redefinition.
+
+#include <string.h>
+#include <unistd.h>
+
+namespace art {
+
+namespace {
+
+/*
+ * Return a set of strings describing available VM features (this is chiefly
+ * of interest to DDMS).
+ */
+jobjectArray VMDebug_getVmFeatureList(JNIEnv* env, jclass) {
+ std::vector<std::string> features;
+ //features.push_back("method-trace-profiling");
+ //features.push_back("method-trace-profiling-streaming");
+ //features.push_back("hprof-heap-dump");
+ //features.push_back("hprof-heap-dump-streaming");
+ return toStringArray(env, features);
+}
+
+void VMDebug_startAllocCounting(JNIEnv*, jclass) {
+ Runtime::Current()->SetStatsEnabled(true);
+}
+
+void VMDebug_stopAllocCounting(JNIEnv*, jclass) {
+ Runtime::Current()->SetStatsEnabled(false);
+}
+
+jint VMDebug_getAllocCount(JNIEnv* env, jclass, jint kind) {
+ return Runtime::Current()->GetStat(kind);
+}
+
+void VMDebug_resetAllocCount(JNIEnv*, jclass, jint kinds) {
+ Runtime::Current()->ResetStats(kinds);
+}
+
+void VMDebug_startMethodTracingDdmsImpl(JNIEnv* env, jclass, jint bufferSize, jint flags) {
+ UNIMPLEMENTED(WARNING);
+ //dvmMethodTraceStart("[DDMS]", -1, bufferSize, flags, true);
+}
+
+void VMDebug_startMethodTracingFd(JNIEnv* env, jclass, jstring javaTraceFilename, jobject javaFd, jint bufferSize, jint flags) {
+ int originalFd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (originalFd < 0) {
+ return;
+ }
+
+ int fd = dup(originalFd);
+ if (fd < 0) {
+ jniThrowExceptionFmt(env, "java/lang/RuntimeException", "dup(%d) failed: %s", originalFd, strerror(errno));
+ return;
+ }
+
+ ScopedUtfChars traceFilename(env, javaTraceFilename);
+ if (traceFilename.c_str() == NULL) {
+ return;
+ }
+ UNIMPLEMENTED(WARNING);
+ //dvmMethodTraceStart(traceFilename.c_str(), fd, bufferSize, flags, false);
+}
+
+void VMDebug_startMethodTracingFilename(JNIEnv* env, jclass, jstring javaTraceFilename, jint bufferSize, jint flags) {
+ ScopedUtfChars traceFilename(env, javaTraceFilename);
+ if (traceFilename.c_str() == NULL) {
+ return;
+ }
+ UNIMPLEMENTED(WARNING);
+ //dvmMethodTraceStart(traceFilename.c_str(), -1, bufferSize, flags, false);
+}
+
+jboolean VMDebug_isMethodTracingActive(JNIEnv*, jclass) {
+ UNIMPLEMENTED(WARNING);
+ return JNI_FALSE; //dvmIsMethodTraceActive();
+}
+
+void VMDebug_stopMethodTracing(JNIEnv*, jclass) {
+ UNIMPLEMENTED(WARNING);
+ //dvmMethodTraceStop();
+}
+
+void VMDebug_startEmulatorTracing(JNIEnv*, jclass) {
+ UNIMPLEMENTED(WARNING);
+ //dvmEmulatorTraceStart();
+}
+
+void VMDebug_stopEmulatorTracing(JNIEnv*, jclass) {
+ UNIMPLEMENTED(WARNING);
+ //dvmEmulatorTraceStop();
+}
+
+jboolean VMDebug_isDebuggerConnected(JNIEnv*, jclass) {
+ UNIMPLEMENTED(WARNING);
+ return JNI_FALSE; //dvmDbgIsDebuggerConnected();
+}
+
+jboolean VMDebug_isDebuggingEnabled(JNIEnv*, jclass) {
+ UNIMPLEMENTED(WARNING);
+ return JNI_FALSE; //return gDvm.jdwpConfigured;
+}
+
+jlong VMDebug_lastDebuggerActivity(JNIEnv*, jclass) {
+ UNIMPLEMENTED(WARNING);
+ return 0; //dvmDbgLastDebuggerActivity();
+}
+
+void VMDebug_startInstructionCounting(JNIEnv* env, jclass) {
+ jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
+}
+
+void VMDebug_stopInstructionCounting(JNIEnv* env, jclass) {
+ jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
+}
+
+void VMDebug_getInstructionCount(JNIEnv* env, jclass, jintArray javaCounts) {
+ jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
+}
+
+void VMDebug_resetInstructionCount(JNIEnv* env, jclass) {
+ jniThrowException(env, "java/lang/UnsupportedOperationException", NULL);
+}
+
+void VMDebug_printLoadedClasses(JNIEnv*, jclass, jint flags) {
+ return Runtime::Current()->GetClassLinker()->DumpAllClasses(flags);
+}
+
+jint VMDebug_getLoadedClassCount(JNIEnv*, jclass) {
+ return Runtime::Current()->GetClassLinker()->NumLoadedClasses();
+}
+
+/*
+ * Returns the thread-specific CPU-time clock value for the current thread,
+ * or -1 if the feature isn't supported.
+ */
+jlong VMDebug_threadCpuTimeNanos(JNIEnv*, jclass) {
+#ifdef HAVE_POSIX_CLOCKS
+ struct timespec now;
+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &now);
+ return static_cast<jlong>(now.tv_sec*1000000000LL + now.tv_nsec);
+#else
+ return -1LL;
+#endif
+}
+
+/*
+ * static void dumpHprofData(String fileName, FileDescriptor fd)
+ *
+ * Cause "hprof" data to be dumped. We can throw an IOException if an
+ * error occurs during file handling.
+ */
+void VMDebug_dumpHprofData(JNIEnv* env, jclass, jstring javaFilename, jobject javaFd) {
+ // Only one of these may be NULL.
+ if (javaFilename == NULL && javaFd == NULL) {
+ jniThrowNullPointerException(env, "fileName == null && fd == null");
+ return;
+ }
+
+ std::string filename;
+ if (javaFilename != NULL) {
+ ScopedUtfChars chars(env, javaFilename);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ filename = chars.c_str();
+ } else {
+ filename = "[fd]";
+ }
+
+ int fd = -1;
+ if (javaFd != NULL) {
+ fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (fd < 0) {
+ jniThrowException(env, "Ljava/lang/RuntimeException;", "Invalid file descriptor");
+ return;
+ }
+ }
+
+ UNIMPLEMENTED(WARNING);
+ int result = 0; //hprofDumpHeap(filename.c_str(), fd, false);
+ if (result != 0) {
+ // TODO: ideally we'd throw something more specific based on actual failure
+ jniThrowException(env, "Ljava/lang/RuntimeException;", "Failure during heap dump; check log output for details");
+ return;
+ }
+}
+
+void VMDebug_dumpHprofDataDdms(JNIEnv* env, jclass) {
+ UNIMPLEMENTED(WARNING);
+ int result = 0; //hprofDumpHeap("[DDMS]", -1, true);
+ if (result != 0) {
+ // TODO: ideally we'd throw something more specific based on actual failure
+ jniThrowException(env, "Ljava/lang/RuntimeException;", "Failure during heap dump; check log output for details");
+ return;
+ }
+}
+
+void VMDebug_dumpReferenceTables(JNIEnv* env, jclass) {
+ LOG(INFO) << "--- reference table dump ---";
+
+ JNIEnvExt* e = reinterpret_cast<JNIEnvExt*>(env);
+ e->DumpReferenceTables();
+ e->vm->DumpReferenceTables();
+
+ LOG(INFO) << "---";
+}
+
+/*
+ * Dump the current thread's interpreted stack and abort the VM. Useful
+ * for seeing both interpreted and native stack traces.
+ */
+void VMDebug_crash(JNIEnv*, jclass) {
+ std::stringstream os;
+ os << "Crashing VM on request:\n";
+ Thread::Current()->Dump(os);
+ LOG(FATAL) << os.str();
+}
+
+/*
+ * Provide a hook for gdb to hang to so that the VM can be stopped when
+ * user-tagged source locations are being executed.
+ */
+void VMDebug_infopoint(JNIEnv*, jclass, jint id) {
+ LOG(INFO) << "VMDebug infopoint " << id << " hit";
+}
+
+jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass, jboolean countAssignable) {
+ Class* c = Decode<Class*>(env, javaClass);
+ if (c == NULL) {
+ return 0;
+ }
+ return Heap::CountInstances(c, countAssignable);
+}
+
+JNINativeMethod gMethods[] = {
+ NATIVE_METHOD(VMDebug, countInstancesOfClass, "(Ljava/lang/Class;Z)J"),
+ NATIVE_METHOD(VMDebug, crash, "()V"),
+ NATIVE_METHOD(VMDebug, dumpHprofData, "(Ljava/lang/String;Ljava/io/FileDescriptor;)V"),
+ NATIVE_METHOD(VMDebug, dumpHprofDataDdms, "()V"),
+ NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"),
+ NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
+ NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"),
+ NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"),
+ NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"),
+ NATIVE_METHOD(VMDebug, infopoint, "(I)V"),
+ NATIVE_METHOD(VMDebug, isDebuggerConnected, "()Z"),
+ NATIVE_METHOD(VMDebug, isDebuggingEnabled, "()Z"),
+ NATIVE_METHOD(VMDebug, isMethodTracingActive, "()Z"),
+ NATIVE_METHOD(VMDebug, lastDebuggerActivity, "()J"),
+ NATIVE_METHOD(VMDebug, printLoadedClasses, "(I)V"),
+ NATIVE_METHOD(VMDebug, resetAllocCount, "(I)V"),
+ NATIVE_METHOD(VMDebug, resetInstructionCount, "()V"),
+ NATIVE_METHOD(VMDebug, startAllocCounting, "()V"),
+ NATIVE_METHOD(VMDebug, startEmulatorTracing, "()V"),
+ NATIVE_METHOD(VMDebug, startInstructionCounting, "()V"),
+ NATIVE_METHOD(VMDebug, startMethodTracingDdmsImpl, "(II)V"),
+ NATIVE_METHOD(VMDebug, startMethodTracingFd, "(Ljava/lang/String;Ljava/io/FileDescriptor;II)V"),
+ NATIVE_METHOD(VMDebug, startMethodTracingFilename, "(Ljava/lang/String;II)V"),
+ NATIVE_METHOD(VMDebug, stopAllocCounting, "()V"),
+ NATIVE_METHOD(VMDebug, stopEmulatorTracing, "()V"),
+ NATIVE_METHOD(VMDebug, stopInstructionCounting, "()V"),
+ NATIVE_METHOD(VMDebug, stopMethodTracing, "()V"),
+ NATIVE_METHOD(VMDebug, threadCpuTimeNanos, "()J"),
+};
+
+} // namespace
+
+void register_dalvik_system_VMDebug(JNIEnv* env) {
+ jniRegisterNativeMethods(env, "dalvik/system/VMDebug", gMethods, NELEM(gMethods));
+}
+
+} // namespace art
diff --git a/src/heap.cc b/src/heap.cc
index 094790a..4d8d176 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -40,6 +40,8 @@
Mutex* Heap::lock_ = NULL;
+bool Heap::verify_objects_ = false;
+
class ScopedHeapLock {
public:
ScopedHeapLock() {
@@ -169,8 +171,6 @@
return true;
}
-bool Heap::verify_objects_ = false;
-
#if VERIFY_OBJECT_ENABLED
void Heap::VerifyObject(const Object* obj) {
if (!verify_objects_) {
@@ -237,6 +237,16 @@
DCHECK_NE(size, 0u);
num_bytes_allocated_ += size;
num_objects_allocated_ += 1;
+
+ if (Runtime::Current()->HasStatsEnabled()) {
+ RuntimeStats* global_stats = Runtime::Current()->GetStats();
+ RuntimeStats* thread_stats = Thread::Current()->GetStats();
+ ++global_stats->allocated_objects;
+ ++thread_stats->allocated_objects;
+ global_stats->allocated_bytes += size;
+ thread_stats->allocated_bytes += size;
+ }
+
live_bitmap_->Set(obj);
}
@@ -253,6 +263,15 @@
if (num_objects_allocated_ > 0) {
num_objects_allocated_ -= 1;
}
+
+ if (Runtime::Current()->HasStatsEnabled()) {
+ RuntimeStats* global_stats = Runtime::Current()->GetStats();
+ RuntimeStats* thread_stats = Thread::Current()->GetStats();
+ ++global_stats->freed_objects;
+ ++thread_stats->freed_objects;
+ global_stats->freed_bytes += size;
+ thread_stats->freed_bytes += size;
+ }
}
void Heap::RecordImageAllocations(Space* space) {
@@ -307,6 +326,10 @@
// Another failure. Our thread was starved or there may be too many
// live objects. Try a foreground GC. This will have no effect if
// the concurrent GC is already running.
+ if (Runtime::Current()->HasStatsEnabled()) {
+ ++Runtime::Current()->GetStats()->gc_for_alloc_count;
+ ++Thread::Current()->GetStats()->gc_for_alloc_count;
+ }
CollectGarbageInternal();
ptr = space->AllocWithoutGrowth(size);
if (ptr != NULL) {
@@ -365,6 +388,46 @@
return 0;
}
+class InstanceCounter {
+ public:
+ InstanceCounter(Class* c, bool count_assignable)
+ : class_(c), count_assignable_(count_assignable), count_(0) {
+ }
+
+ size_t GetCount() {
+ return count_;
+ }
+
+ static void Callback(Object* o, void* arg) {
+ reinterpret_cast<InstanceCounter*>(arg)->VisitInstance(o);
+ }
+
+ private:
+ void VisitInstance(Object* o) {
+ Class* instance_class = o->GetClass();
+ if (count_assignable_) {
+ if (instance_class == class_) {
+ ++count_;
+ }
+ } else {
+ if (instance_class != NULL && class_->IsAssignableFrom(instance_class)) {
+ ++count_;
+ }
+ }
+ }
+
+ Class* class_;
+ bool count_assignable_;
+ size_t count_;
+};
+
+int64_t Heap::CountInstances(Class* c, bool count_assignable) {
+ ScopedHeapLock lock;
+ InstanceCounter counter(c, count_assignable);
+ live_bitmap_->Walk(InstanceCounter::Callback, &counter);
+ return counter.GetCount();
+}
+
void Heap::CollectGarbage() {
ScopedHeapLock lock;
CollectGarbageInternal();
diff --git a/src/heap.h b/src/heap.h
index e12ef4a..f39454a 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -1,4 +1,18 @@
-// Copyright 2011 Google Inc. All Rights Reserved.
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
#ifndef ART_SRC_HEAP_H_
#define ART_SRC_HEAP_H_
@@ -64,6 +78,9 @@
// Implements java.lang.Runtime.freeMemory.
static int64_t GetFreeMemory();
+ // Implements VMDebug.countInstancesOfClass.
+ static int64_t CountInstances(Class* c, bool count_assignable);
+
// Implements dalvik.system.VMRuntime.clearGrowthLimit.
static void ClearGrowthLimit() {
UNIMPLEMENTED(WARNING);
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 6c25f6e..f9fcb78 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -2537,6 +2537,11 @@
JNIEnvExt::~JNIEnvExt() {
}
+void JNIEnvExt::DumpReferenceTables() {
+ locals.Dump();
+ monitors.Dump();
+}
+
// JNI Invocation interface.
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, void** p_env, void* vm_args) {
@@ -2672,6 +2677,21 @@
delete libraries;
}
+void JavaVMExt::DumpReferenceTables() {
+ {
+ MutexLock mu(globals_lock);
+ globals.Dump();
+ }
+ {
+ MutexLock mu(weak_globals_lock);
+ weak_globals.Dump();
+ }
+ {
+ MutexLock mu(pins_lock);
+ pin_table.Dump();
+ }
+}
+
bool JavaVMExt::LoadNativeLibrary(const std::string& path, ClassLoader* class_loader, std::string& detail) {
detail.clear();
diff --git a/src/jni_internal.h b/src/jni_internal.h
index 38e38fc..736e5db 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -62,6 +62,8 @@
*/
void* FindCodeForNativeMethod(Method* m);
+ void DumpReferenceTables();
+
void VisitRoots(Heap::RootVisitor*, void*);
Runtime* runtime;
@@ -104,6 +106,8 @@
JNIEnvExt(Thread* self, JavaVMExt* vm);
~JNIEnvExt();
+ void DumpReferenceTables();
+
Thread* const self;
JavaVMExt* vm;
diff --git a/src/object.cc b/src/object.cc
index b0a6745..b94d392 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -5,6 +5,7 @@
#include <string.h>
#include <algorithm>
+#include <iostream>
#include <string>
#include <utility>
@@ -710,6 +711,63 @@
return Heap::AllocObject(this, this->object_size_);
}
+void Class::DumpClass(std::ostream& os, int flags) {
+ if ((flags & kDumpClassFullDetail) == 0) {
+ os << PrettyClass(this);
+ if ((flags & kDumpClassClassLoader) != 0) {
+ os << ' ' << GetClassLoader();
+ }
+ if ((flags & kDumpClassInitialized) != 0) {
+ os << ' ' << GetStatus();
+ }
+ os << std::endl;
+ return;
+ }
+
+ Class* super = GetSuperClass();
+ os << "----- " << (IsInterface() ? "interface" : "class") << " "
+ << "'" << GetDescriptor()->ToModifiedUtf8() << "' cl=" << GetClassLoader() << " -----\n",
+ os << " objectSize=" << SizeOf() << " "
+ << "(" << (super != NULL ? super->SizeOf() : -1) << " from super)\n",
+ os << StringPrintf(" access=0x%04x.%04x\n",
+ GetAccessFlags() >> 16, GetAccessFlags() & kAccJavaFlagsMask);
+ if (super != NULL) {
+ os << " super='" << PrettyClass(super) << "' (cl=" << super->GetClassLoader() << ")\n";
+ }
+ if (IsArrayClass()) {
+ os << " componentType=" << PrettyClass(GetComponentType()) << "\n";
+ }
+ if (NumInterfaces() > 0) {
+ os << " interfaces (" << NumInterfaces() << "):\n";
+ for (size_t i = 0; i < NumInterfaces(); ++i) {
+ Class* interface = GetInterface(i);
+ const ClassLoader* cl = interface->GetClassLoader();
+ os << StringPrintf(" %2d: %s (cl=%p)\n", i, PrettyClass(interface).c_str(), cl);
+ }
+ }
+ os << " vtable (" << NumVirtualMethods() << " entries, "
+ << (super != NULL ? super->NumVirtualMethods() : 0) << " in super):\n";
+ for (size_t i = 0; i < NumVirtualMethods(); ++i) {
+ os << StringPrintf(" %2d: %s\n", i, PrettyMethod(GetVirtualMethod(i)).c_str());
+ }
+ os << " direct methods (" << NumDirectMethods() << " entries):\n";
+ for (size_t i = 0; i < NumDirectMethods(); ++i) {
+ os << StringPrintf(" %2d: %s\n", i, PrettyMethod(GetDirectMethod(i)).c_str());
+ }
+ if (NumStaticFields() > 0) {
+ os << " static fields (" << NumStaticFields() << " entries):\n";
+ for (size_t i = 0; i < NumStaticFields(); ++i) {
+ os << StringPrintf(" %2d: %s\n", i, PrettyField(GetStaticField(i)).c_str());
+ }
+ }
+ if (NumInstanceFields() > 0) {
+ os << " instance fields (" << NumInstanceFields() << " entries):\n";
+ for (size_t i = 0; i < NumInstanceFields(); ++i) {
+ os << StringPrintf(" %2d: %s\n", i, PrettyField(GetInstanceField(i)).c_str());
+ }
+ }
+}
+
void Class::SetReferenceInstanceOffsets(uint32_t new_reference_offsets) {
if (new_reference_offsets != CLASS_WALK_SUPER) {
// Sanity check that the number of bits set in the reference offset bitmap
diff --git a/src/object.h b/src/object.h
index 2a4078d..6f37d5a 100644
--- a/src/object.h
+++ b/src/object.h
@@ -17,6 +17,7 @@
#ifndef ART_SRC_OBJECT_H_
#define ART_SRC_OBJECT_H_
+#include <iosfwd>
#include <vector>
#include "UniquePtr.h"
@@ -1551,6 +1552,14 @@
return MemberOffset(OFFSETOF_MEMBER(Class, dex_cache_));
}
+ enum {
+ kDumpClassFullDetail = 1,
+ kDumpClassClassLoader = (1 << 1),
+ kDumpClassInitialized = (1 << 2),
+ };
+
+ void DumpClass(std::ostream& os, int flags);
+
DexCache* GetDexCache() const;
void SetDexCache(DexCache* new_dex_cache);
diff --git a/src/runtime.cc b/src/runtime.cc
index 1f6a06a..68f42af 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -34,7 +34,8 @@
started_(false),
vfprintf_(NULL),
exit_(NULL),
- abort_(NULL) {
+ abort_(NULL),
+ stats_enabled_(false) {
}
Runtime::~Runtime() {
@@ -451,7 +452,7 @@
void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
#define REGISTER(FN) extern void FN(JNIEnv*); FN(env)
//REGISTER(register_dalvik_system_DexFile);
- //REGISTER(register_dalvik_system_VMDebug);
+ REGISTER(register_dalvik_system_VMDebug);
REGISTER(register_dalvik_system_VMRuntime);
REGISTER(register_dalvik_system_VMStack);
//REGISTER(register_dalvik_system_Zygote);
@@ -491,6 +492,60 @@
thread_list_->Dump(os);
}
+void Runtime::SetStatsEnabled(bool new_state) {
+ if (new_state == true) {
+ GetStats()->Clear(~0);
+ // TODO: wouldn't it make more sense to clear _all_ threads' stats?
+ Thread::Current()->GetStats()->Clear(~0);
+ }
+ stats_enabled_ = new_state;
+}
+
+void Runtime::ResetStats(int kinds) {
+ GetStats()->Clear(kinds & 0xffff);
+ // TODO: wouldn't it make more sense to clear _all_ threads' stats?
+ Thread::Current()->GetStats()->Clear(kinds >> 16);
+}
+
+RuntimeStats* Runtime::GetStats() {
+ return &stats_;
+}
+
+int32_t Runtime::GetStat(int kind) {
+ RuntimeStats* stats;
+ if (kind < (1<<16)) {
+ stats = GetStats();
+ } else {
+ stats = Thread::Current()->GetStats();
+ kind >>= 16;
+ }
+ switch (kind) {
+ case KIND_ALLOCATED_OBJECTS:
+ return stats->allocated_objects;
+ case KIND_ALLOCATED_BYTES:
+ return stats->allocated_bytes;
+ case KIND_FREED_OBJECTS:
+ return stats->freed_objects;
+ case KIND_FREED_BYTES:
+ return stats->freed_bytes;
+ case KIND_GC_INVOCATIONS:
+ return stats->gc_for_alloc_count;
+ case KIND_CLASS_INIT_COUNT:
+ return stats->class_init_count;
+ case KIND_CLASS_INIT_TIME:
+ // Convert ns to us, reduce to 32 bits.
+ return (int) (stats->class_init_time_ns / 1000);
+ case KIND_EXT_ALLOCATED_OBJECTS:
+ case KIND_EXT_ALLOCATED_BYTES:
+ case KIND_EXT_FREED_OBJECTS:
+ case KIND_EXT_FREED_BYTES:
+ return 0; // backward compatibility
+ default:
+ CHECK(false);
+ return -1; // unreachable
+ }
+}
+
void Runtime::BlockSignals() {
sigset_t sigset;
if (sigemptyset(&sigset) == -1) {
diff --git a/src/runtime.h b/src/runtime.h
index 70bdc99..b3d3ebf 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -15,6 +15,7 @@
#include "heap.h"
#include "globals.h"
#include "macros.h"
+#include "runtime_stats.h"
#include "stringpiece.h"
#include "unordered_set.h"
@@ -153,6 +154,18 @@
jni_stub_array_ = jni_stub_array;
}
+ int32_t GetStat(int kind);
+
+ RuntimeStats* GetStats();
+
+ bool HasStatsEnabled() const {
+ return stats_enabled_;
+ }
+
+ void ResetStats(int kinds);
+
+ void SetStatsEnabled(bool new_state);
+
private:
static void PlatformAbort(const char*, int);
@@ -192,6 +205,9 @@
void (*exit_)(jint status);
void (*abort_)();
+ bool stats_enabled_;
+ RuntimeStats stats_;
+
// A pointer to the active runtime or NULL.
static Runtime* instance_;
diff --git a/src/runtime_stats.h b/src/runtime_stats.h
new file mode 100644
index 0000000..2d2bf0c
--- /dev/null
+++ b/src/runtime_stats.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_SRC_RUNTIME_STATS_H_
+#define ART_SRC_RUNTIME_STATS_H_
+
+#include <stdint.h>
+
+namespace art {
+
+// These must match the values in dalvik.system.VMDebug.
+enum StatKinds {
+ KIND_ALLOCATED_OBJECTS = 1<<0,
+ KIND_ALLOCATED_BYTES = 1<<1,
+ KIND_FREED_OBJECTS = 1<<2,
+ KIND_FREED_BYTES = 1<<3,
+ KIND_GC_INVOCATIONS = 1<<4,
+ KIND_CLASS_INIT_COUNT = 1<<5,
+ KIND_CLASS_INIT_TIME = 1<<6,
+
+ // These values exist for backward compatibility.
+ KIND_EXT_ALLOCATED_OBJECTS = 1<<12,
+ KIND_EXT_ALLOCATED_BYTES = 1<<13,
+ KIND_EXT_FREED_OBJECTS = 1<<14,
+ KIND_EXT_FREED_BYTES = 1<<15,
+
+ KIND_GLOBAL_ALLOCATED_OBJECTS = KIND_ALLOCATED_OBJECTS,
+ KIND_GLOBAL_ALLOCATED_BYTES = KIND_ALLOCATED_BYTES,
+ KIND_GLOBAL_FREED_OBJECTS = KIND_FREED_OBJECTS,
+ KIND_GLOBAL_FREED_BYTES = KIND_FREED_BYTES,
+ KIND_GLOBAL_GC_INVOCATIONS = KIND_GC_INVOCATIONS,
+ KIND_GLOBAL_CLASS_INIT_COUNT = KIND_CLASS_INIT_COUNT,
+ KIND_GLOBAL_CLASS_INIT_TIME = KIND_CLASS_INIT_TIME,
+
+ KIND_THREAD_ALLOCATED_OBJECTS = KIND_ALLOCATED_OBJECTS << 16,
+ KIND_THREAD_ALLOCATED_BYTES = KIND_ALLOCATED_BYTES << 16,
+ KIND_THREAD_FREED_OBJECTS = KIND_FREED_OBJECTS << 16,
+ KIND_THREAD_FREED_BYTES = KIND_FREED_BYTES << 16,
+
+ KIND_THREAD_GC_INVOCATIONS = KIND_GC_INVOCATIONS << 16,
+
+ // TODO: failedAllocCount, failedAllocSize
+};
+
+/*
+ * Memory allocation profiler state. This is used both globally and
+ * per-thread.
+ */
+struct PACKED RuntimeStats {
+ RuntimeStats() {
+ Clear(~0);
+ }
+
+ void Clear(int flags) {
+ if ((flags & KIND_ALLOCATED_OBJECTS) != 0) {
+ allocated_objects = 0;
+ }
+ if ((flags & KIND_ALLOCATED_BYTES) != 0) {
+ allocated_bytes = 0;
+ }
+ if ((flags & KIND_FREED_OBJECTS) != 0) {
+ freed_objects = 0;
+ }
+ if ((flags & KIND_FREED_BYTES) != 0) {
+ freed_bytes = 0;
+ }
+ if ((flags & KIND_GC_INVOCATIONS) != 0) {
+ gc_for_alloc_count = 0;
+ }
+ if ((flags & KIND_CLASS_INIT_COUNT) != 0) {
+ class_init_count = 0;
+ }
+ if ((flags & KIND_CLASS_INIT_TIME) != 0) {
+ class_init_time_ns = 0;
+ }
+ }
+
+ // Number of objects allocated.
+ int allocated_objects;
+ // Cumulative size of all objects allocated.
+ int allocated_bytes;
+
+ // Number of objects freed.
+ int freed_objects;
+ // Cumulative size of all freed objects.
+ int freed_bytes;
+
+ // Number of times an allocation triggered a GC.
+ int gc_for_alloc_count;
+
+ // Number of initialized classes.
+ int class_init_count;
+ // Cumulative time spent in class initialization.
+ uint64_t class_init_time_ns;
+
+ DISALLOW_COPY_AND_ASSIGN(RuntimeStats);
+};
+
+} // namespace art
+
+#endif // ART_SRC_HEAP_H_
diff --git a/src/thread.h b/src/thread.h
index 0be4aa6..5a0c4f0 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -32,6 +32,7 @@
#include "mutex.h"
#include "mem_map.h"
#include "offsets.h"
+#include "runtime_stats.h"
#include "UniquePtr.h"
namespace art {
@@ -311,6 +312,10 @@
return peer_;
}
+ RuntimeStats* GetStats() {
+ return &stats_;
+ }
+
// Returns the Method* for the current method.
// This is used by the JNI implementation for logging and diagnostic purposes.
const Method* GetCurrentMethod() const {
@@ -566,6 +571,8 @@
friend class Monitor;
+ RuntimeStats stats_;
+
// FIXME: placeholder for the gc cardTable
uint32_t card_table_;