Fix local reference leaks in debugger and use a cache.

Changed alloc record stack trace element to use jmethodID instead of
JNI weak global references. Added code to delete the local ref
created in AllocRecord::SetType.

Bug: 15886342
External bug: https://code.google.com/p/android/issues/detail?id=72330

Change-Id: Id18e765820baad02246768dc9d633aada60f4fed
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index f19c353..11415b4 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -74,18 +74,13 @@
   }
 
   mirror::ArtMethod* Method() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::ArtMethod* method = reinterpret_cast<mirror::ArtMethod*>(
-        Thread::Current()->DecodeJObject(method_));
-    return method;
+    ScopedObjectAccessUnchecked soa(Thread::Current());
+    return soa.DecodeMethod(method_);
   }
 
   void SetMethod(mirror::ArtMethod* m) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     ScopedObjectAccessUnchecked soa(Thread::Current());
-    JNIEnv* env = soa.Env();
-    if (method_ != nullptr) {
-      env->DeleteWeakGlobalRef(method_);
-    }
-    method_ = env->NewWeakGlobalRef(soa.AddLocalReference<jobject>(m));
+    method_ = soa.EncodeMethod(m);
   }
 
   uint32_t DexPc() const {
@@ -97,27 +92,46 @@
   }
 
  private:
-  jobject method_;  // This is a weak global.
+  jmethodID method_;
   uint32_t dex_pc_;
 };
 
+jobject Dbg::TypeCache::Add(mirror::Class* t) {
+  ScopedObjectAccessUnchecked soa(Thread::Current());
+  int32_t hash_code = t->IdentityHashCode();
+  auto range = objects_.equal_range(hash_code);
+  for (auto it = range.first; it != range.second; ++it) {
+    if (soa.Decode<mirror::Class*>(it->second) == t) {
+      // Found a matching weak global, return it.
+      return it->second;
+    }
+  }
+  JNIEnv* env = soa.Env();
+  const jobject local_ref = soa.AddLocalReference<jobject>(t);
+  const jobject weak_global = env->NewWeakGlobalRef(local_ref);
+  env->DeleteLocalRef(local_ref);
+  objects_.insert(std::make_pair(hash_code, weak_global));
+  return weak_global;
+}
+
+void Dbg::TypeCache::Clear() {
+  ScopedObjectAccess soa(Thread::Current());
+  for (const auto& p : objects_) {
+    soa.Vm()->DeleteWeakGlobalRef(soa.Self(), p.second);
+  }
+  objects_.clear();
+}
+
 class AllocRecord {
  public:
   AllocRecord() : type_(nullptr), byte_count_(0), thin_lock_id_(0) {}
 
   mirror::Class* Type() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    mirror::Class* type = reinterpret_cast<mirror::Class*>(
-        Thread::Current()->DecodeJObject(type_));
-    return type;
+    return down_cast<mirror::Class*>(Thread::Current()->DecodeJObject(type_));
   }
 
   void SetType(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    ScopedObjectAccessUnchecked soa(Thread::Current());
-    JNIEnv* env = soa.Env();
-    if (type_ != nullptr) {
-      env->DeleteWeakGlobalRef(type_);
-    }
-    type_ = env->NewWeakGlobalRef(soa.AddLocalReference<jobject>(t));
+    type_ = Dbg::GetTypeCache().Add(t);
   }
 
   size_t GetDepth() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
@@ -274,6 +288,7 @@
 size_t Dbg::alloc_record_max_ = 0;
 size_t Dbg::alloc_record_head_ = 0;
 size_t Dbg::alloc_record_count_ = 0;
+Dbg::TypeCache Dbg::type_cache_;
 
 // Deoptimization support.
 Mutex* Dbg::deoptimization_lock_ = nullptr;
@@ -4253,8 +4268,10 @@
     Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
     {
       MutexLock mu(Thread::Current(), *alloc_tracker_lock_);
+      LOG(INFO) << "Disabling alloc tracker";
       delete[] recent_allocation_records_;
       recent_allocation_records_ = NULL;
+      type_cache_.Clear();
     }
   }
 }
@@ -4376,8 +4393,12 @@
   StringTable() {
   }
 
-  void Add(const char* s) {
-    table_.insert(s);
+  void Add(const std::string& str) {
+    table_.insert(str);
+  }
+
+  void Add(const char* str) {
+    table_.insert(str);
   }
 
   size_t IndexOf(const char* s) const {
@@ -4476,9 +4497,7 @@
     int idx = HeadIndex();
     while (count--) {
       AllocRecord* record = &recent_allocation_records_[idx];
-
-      class_names.Add(record->Type()->GetDescriptor().c_str());
-
+      class_names.Add(record->Type()->GetDescriptor());
       for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
         mirror::ArtMethod* m = record->StackElement(i)->Method();
         if (m != NULL) {
diff --git a/runtime/debugger.h b/runtime/debugger.h
index 1cf0b0c..2589638 100644
--- a/runtime/debugger.h
+++ b/runtime/debugger.h
@@ -23,6 +23,7 @@
 
 #include <pthread.h>
 
+#include <map>
 #include <set>
 #include <string>
 #include <vector>
@@ -160,6 +161,17 @@
 
 class Dbg {
  public:
+  class TypeCache {
+   public:
+    // Returns a weak global for the input type. Deduplicates.
+    jobject Add(mirror::Class* t) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+    // Clears the type cache and deletes all the weak global refs.
+    void Clear();
+
+   private:
+    std::multimap<int32_t, jobject> objects_;
+  };
+
   static bool ParseJdwpOptions(const std::string& options);
   static void SetJdwpAllowed(bool allowed);
 
@@ -555,6 +567,10 @@
   static void DdmSendHeapSegments(bool native)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  static TypeCache& GetTypeCache() {
+    return type_cache_;
+  }
+
  private:
   static void DdmBroadcast(bool connect) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   static void PostThreadStartOrStop(Thread*, uint32_t)
@@ -604,6 +620,9 @@
 
   static size_t* GetReferenceCounterForEvent(uint32_t instrumentation_event);
 
+  // Weak global type cache, TODO improve this.
+  static TypeCache type_cache_;
+
   // Instrumentation event reference counters.
   // TODO we could use an array instead of having all these dedicated counters. Instrumentation
   // events are bits of a mask so we could convert them to array index.