Merge "Let dalvikvm default based on persist.sys.dalvik.vm.lib" into dalvik-dev
diff --git a/src/compiler/dex/mir_dataflow.cc b/src/compiler/dex/mir_dataflow.cc
index 3b2c1a6..c3680d6 100644
--- a/src/compiler/dex/mir_dataflow.cc
+++ b/src/compiler/dex/mir_dataflow.cc
@@ -373,7 +373,7 @@
   DF_FORMAT_35C | DF_UMS,
 
   // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA}
-  DF_FORMAT_35C | DF_UMS,
+  DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
 
   // 73 UNUSED_73
   DF_NOP,
@@ -391,7 +391,7 @@
   DF_FORMAT_3RC | DF_UMS,
 
   // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN}
-  DF_FORMAT_3RC | DF_UMS,
+  DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
 
   // 79 UNUSED_79
   DF_NOP,
diff --git a/src/compiler/sea_ir/instruction_tools.cc b/src/compiler/sea_ir/instruction_tools.cc
index 68be589..5433591 100644
--- a/src/compiler/sea_ir/instruction_tools.cc
+++ b/src/compiler/sea_ir/instruction_tools.cc
@@ -369,7 +369,7 @@
   DF_FORMAT_35C | DF_UMS,
 
   // 72 INVOKE_INTERFACE {vD, vE, vF, vG, vA}
-  DF_FORMAT_35C | DF_UMS,
+  DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
 
   // 73 UNUSED_73
   DF_NOP,
@@ -387,7 +387,7 @@
   DF_FORMAT_3RC | DF_UMS,
 
   // 78 INVOKE_INTERFACE_RANGE {vCCCC .. vNNNN}
-  DF_FORMAT_3RC | DF_UMS,
+  DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
 
   // 79 UNUSED_79
   DF_NOP,
diff --git a/src/debugger.cc b/src/debugger.cc
index f2a10f0..4e89838 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -20,6 +20,7 @@
 
 #include <set>
 
+#include "cutils/properties.h"
 #include "class_linker.h"
 #include "class_linker-inl.h"
 #include "dex_file-inl.h"
@@ -53,7 +54,7 @@
 namespace art {
 
 static const size_t kMaxAllocRecordStackDepth = 16; // Max 255.
-static const size_t kNumAllocRecords = 512; // Must be a power of 2.
+static const size_t kDefaultNumAllocRecords = 64*1024; // Must be a power of 2.
 
 struct AllocRecordStackTraceElement {
   mirror::AbstractMethod* method;
@@ -183,6 +184,7 @@
 // Recent allocation tracking.
 static Mutex gAllocTrackerLock DEFAULT_MUTEX_ACQUIRED_AFTER ("AllocTracker lock");
 AllocRecord* Dbg::recent_allocation_records_ PT_GUARDED_BY(gAllocTrackerLock) = NULL; // TODO: CircularBuffer<AllocRecord>
+static size_t gAllocRecordMax GUARDED_BY(gAllocTrackerLock) = 0;
 static size_t gAllocRecordHead GUARDED_BY(gAllocTrackerLock) = 0;
 static size_t gAllocRecordCount GUARDED_BY(gAllocTrackerLock) = 0;
 
@@ -3437,15 +3439,38 @@
   Dbg::DdmSendChunk(native ? CHUNK_TYPE("NHEN") : CHUNK_TYPE("HPEN"), sizeof(heap_id), heap_id);
 }
 
+static size_t GetAllocTrackerMax() {
+#ifdef HAVE_ANDROID_OS
+  // Check whether there's a system property overriding the number of records.
+  const char* propertyName = "dalvik.vm.allocTrackerMax";
+  char allocRecordMaxString[PROPERTY_VALUE_MAX];
+  if (property_get(propertyName, allocRecordMaxString, "") > 0) {
+    char* end;
+    size_t value = strtoul(allocRecordMaxString, &end, 10);
+    if (*end != '\0') {
+      ALOGE("Ignoring %s '%s' --- invalid", propertyName, allocRecordMaxString);
+      return kDefaultNumAllocRecords;
+    }
+    if (!IsPowerOfTwo(value)) {
+      ALOGE("Ignoring %s '%s' --- not power of two", propertyName, allocRecordMaxString);
+      return kDefaultNumAllocRecords;
+    }
+    return value;
+  }
+#endif
+  return kDefaultNumAllocRecords;
+}
+
 void Dbg::SetAllocTrackingEnabled(bool enabled) {
   MutexLock mu(Thread::Current(), gAllocTrackerLock);
   if (enabled) {
     if (recent_allocation_records_ == NULL) {
-      LOG(INFO) << "Enabling alloc tracker (" << kNumAllocRecords << " entries, "
-                << kMaxAllocRecordStackDepth << " frames --> "
-                << (sizeof(AllocRecord) * kNumAllocRecords) << " bytes)";
+      gAllocRecordMax = GetAllocTrackerMax();
+      LOG(INFO) << "Enabling alloc tracker (" << gAllocRecordMax << " entries of "
+                << kMaxAllocRecordStackDepth << " frames, taking "
+                << PrettySize(sizeof(AllocRecord) * gAllocRecordMax) << ")";
       gAllocRecordHead = gAllocRecordCount = 0;
-      recent_allocation_records_ = new AllocRecord[kNumAllocRecords];
+      recent_allocation_records_ = new AllocRecord[gAllocRecordMax];
       CHECK(recent_allocation_records_ != NULL);
     }
   } else {
@@ -3496,7 +3521,7 @@
   }
 
   // Advance and clip.
-  if (++gAllocRecordHead == kNumAllocRecords) {
+  if (++gAllocRecordHead == gAllocRecordMax) {
     gAllocRecordHead = 0;
   }
 
@@ -3510,7 +3535,7 @@
   AllocRecordStackVisitor visitor(self, record);
   visitor.WalkStack();
 
-  if (gAllocRecordCount < kNumAllocRecords) {
+  if (gAllocRecordCount < gAllocRecordMax) {
     ++gAllocRecordCount;
   }
 }
@@ -3522,9 +3547,9 @@
 // from it.
 //
 // We need to handle underflow in our circular buffer, so we add
-// kNumAllocRecords and then mask it back down.
+// gAllocRecordMax and then mask it back down.
 static inline int HeadIndex() EXCLUSIVE_LOCKS_REQUIRED(gAllocTrackerLock) {
-  return (gAllocRecordHead+1 + kNumAllocRecords - gAllocRecordCount) & (kNumAllocRecords-1);
+  return (gAllocRecordHead+1 + gAllocRecordMax - gAllocRecordCount) & (gAllocRecordMax-1);
 }
 
 void Dbg::DumpRecentAllocations() {
@@ -3560,7 +3585,7 @@
       usleep(40000);
     }
 
-    i = (i + 1) & (kNumAllocRecords-1);
+    i = (i + 1) & (gAllocRecordMax-1);
   }
 }
 
@@ -3632,7 +3657,7 @@
  * followed by UTF-16 data.
  *
  * We send up 16-bit unsigned indexes into string tables.  In theory there
- * can be (kMaxAllocRecordStackDepth * kNumAllocRecords) unique strings in
+ * can be (kMaxAllocRecordStackDepth * gAllocRecordMax) unique strings in
  * each table, but in practice there should be far fewer.
  *
  * The chief reason for using a string table here is to keep the size of
@@ -3650,111 +3675,111 @@
   }
 
   Thread* self = Thread::Current();
-  MutexLock mu(self, gAllocTrackerLock);
-
-  //
-  // Part 1: generate string tables.
-  //
-  StringTable class_names;
-  StringTable method_names;
-  StringTable filenames;
-
-  int count = gAllocRecordCount;
-  int idx = HeadIndex();
-  while (count--) {
-    AllocRecord* record = &recent_allocation_records_[idx];
-
-    class_names.Add(ClassHelper(record->type).GetDescriptor());
-
-    MethodHelper mh;
-    for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
-      mirror::AbstractMethod* m = record->stack[i].method;
-      if (m != NULL) {
-        mh.ChangeMethod(m);
-        class_names.Add(mh.GetDeclaringClassDescriptor());
-        method_names.Add(mh.GetName());
-        filenames.Add(mh.GetDeclaringClassSourceFile());
-      }
-    }
-
-    idx = (idx + 1) & (kNumAllocRecords-1);
-  }
-
-  LOG(INFO) << "allocation records: " << gAllocRecordCount;
-
-  //
-  // Part 2: allocate a buffer and generate the output.
-  //
   std::vector<uint8_t> bytes;
+  {
+    MutexLock mu(self, gAllocTrackerLock);
+    //
+    // Part 1: generate string tables.
+    //
+    StringTable class_names;
+    StringTable method_names;
+    StringTable filenames;
 
-  // (1b) message header len (to allow future expansion); includes itself
-  // (1b) entry header len
-  // (1b) stack frame len
-  const int kMessageHeaderLen = 15;
-  const int kEntryHeaderLen = 9;
-  const int kStackFrameLen = 8;
-  JDWP::Append1BE(bytes, kMessageHeaderLen);
-  JDWP::Append1BE(bytes, kEntryHeaderLen);
-  JDWP::Append1BE(bytes, kStackFrameLen);
+    int count = gAllocRecordCount;
+    int idx = HeadIndex();
+    while (count--) {
+      AllocRecord* record = &recent_allocation_records_[idx];
 
-  // (2b) number of entries
-  // (4b) offset to string table from start of message
-  // (2b) number of class name strings
-  // (2b) number of method name strings
-  // (2b) number of source file name strings
-  JDWP::Append2BE(bytes, gAllocRecordCount);
-  size_t string_table_offset = bytes.size();
-  JDWP::Append4BE(bytes, 0); // We'll patch this later...
-  JDWP::Append2BE(bytes, class_names.Size());
-  JDWP::Append2BE(bytes, method_names.Size());
-  JDWP::Append2BE(bytes, filenames.Size());
+      class_names.Add(ClassHelper(record->type).GetDescriptor());
 
-  count = gAllocRecordCount;
-  idx = HeadIndex();
-  ClassHelper kh;
-  while (count--) {
-    // For each entry:
-    // (4b) total allocation size
-    // (2b) thread id
-    // (2b) allocated object's class name index
-    // (1b) stack depth
-    AllocRecord* record = &recent_allocation_records_[idx];
-    size_t stack_depth = record->GetDepth();
-    kh.ChangeClass(record->type);
-    size_t allocated_object_class_name_index = class_names.IndexOf(kh.GetDescriptor());
-    JDWP::Append4BE(bytes, record->byte_count);
-    JDWP::Append2BE(bytes, record->thin_lock_id);
-    JDWP::Append2BE(bytes, allocated_object_class_name_index);
-    JDWP::Append1BE(bytes, stack_depth);
+      MethodHelper mh;
+      for (size_t i = 0; i < kMaxAllocRecordStackDepth; i++) {
+        mirror::AbstractMethod* m = record->stack[i].method;
+        if (m != NULL) {
+          mh.ChangeMethod(m);
+          class_names.Add(mh.GetDeclaringClassDescriptor());
+          method_names.Add(mh.GetName());
+          filenames.Add(mh.GetDeclaringClassSourceFile());
+        }
+      }
 
-    MethodHelper mh;
-    for (size_t stack_frame = 0; stack_frame < stack_depth; ++stack_frame) {
-      // For each stack frame:
-      // (2b) method's class name
-      // (2b) method name
-      // (2b) method source file
-      // (2b) line number, clipped to 32767; -2 if native; -1 if no source
-      mh.ChangeMethod(record->stack[stack_frame].method);
-      size_t class_name_index = class_names.IndexOf(mh.GetDeclaringClassDescriptor());
-      size_t method_name_index = method_names.IndexOf(mh.GetName());
-      size_t file_name_index = filenames.IndexOf(mh.GetDeclaringClassSourceFile());
-      JDWP::Append2BE(bytes, class_name_index);
-      JDWP::Append2BE(bytes, method_name_index);
-      JDWP::Append2BE(bytes, file_name_index);
-      JDWP::Append2BE(bytes, record->stack[stack_frame].LineNumber());
+      idx = (idx + 1) & (gAllocRecordMax-1);
     }
 
-    idx = (idx + 1) & (kNumAllocRecords-1);
+    LOG(INFO) << "allocation records: " << gAllocRecordCount;
+
+    //
+    // Part 2: Generate the output and store it in the buffer.
+    //
+
+    // (1b) message header len (to allow future expansion); includes itself
+    // (1b) entry header len
+    // (1b) stack frame len
+    const int kMessageHeaderLen = 15;
+    const int kEntryHeaderLen = 9;
+    const int kStackFrameLen = 8;
+    JDWP::Append1BE(bytes, kMessageHeaderLen);
+    JDWP::Append1BE(bytes, kEntryHeaderLen);
+    JDWP::Append1BE(bytes, kStackFrameLen);
+
+    // (2b) number of entries
+    // (4b) offset to string table from start of message
+    // (2b) number of class name strings
+    // (2b) number of method name strings
+    // (2b) number of source file name strings
+    JDWP::Append2BE(bytes, gAllocRecordCount);
+    size_t string_table_offset = bytes.size();
+    JDWP::Append4BE(bytes, 0); // We'll patch this later...
+    JDWP::Append2BE(bytes, class_names.Size());
+    JDWP::Append2BE(bytes, method_names.Size());
+    JDWP::Append2BE(bytes, filenames.Size());
+
+    count = gAllocRecordCount;
+    idx = HeadIndex();
+    ClassHelper kh;
+    while (count--) {
+      // For each entry:
+      // (4b) total allocation size
+      // (2b) thread id
+      // (2b) allocated object's class name index
+      // (1b) stack depth
+      AllocRecord* record = &recent_allocation_records_[idx];
+      size_t stack_depth = record->GetDepth();
+      kh.ChangeClass(record->type);
+      size_t allocated_object_class_name_index = class_names.IndexOf(kh.GetDescriptor());
+      JDWP::Append4BE(bytes, record->byte_count);
+      JDWP::Append2BE(bytes, record->thin_lock_id);
+      JDWP::Append2BE(bytes, allocated_object_class_name_index);
+      JDWP::Append1BE(bytes, stack_depth);
+
+      MethodHelper mh;
+      for (size_t stack_frame = 0; stack_frame < stack_depth; ++stack_frame) {
+        // For each stack frame:
+        // (2b) method's class name
+        // (2b) method name
+        // (2b) method source file
+        // (2b) line number, clipped to 32767; -2 if native; -1 if no source
+        mh.ChangeMethod(record->stack[stack_frame].method);
+        size_t class_name_index = class_names.IndexOf(mh.GetDeclaringClassDescriptor());
+        size_t method_name_index = method_names.IndexOf(mh.GetName());
+        size_t file_name_index = filenames.IndexOf(mh.GetDeclaringClassSourceFile());
+        JDWP::Append2BE(bytes, class_name_index);
+        JDWP::Append2BE(bytes, method_name_index);
+        JDWP::Append2BE(bytes, file_name_index);
+        JDWP::Append2BE(bytes, record->stack[stack_frame].LineNumber());
+      }
+
+      idx = (idx + 1) & (gAllocRecordMax-1);
+    }
+
+    // (xb) class name strings
+    // (xb) method name strings
+    // (xb) source file strings
+    JDWP::Set4BE(&bytes[string_table_offset], bytes.size());
+    class_names.WriteTo(bytes);
+    method_names.WriteTo(bytes);
+    filenames.WriteTo(bytes);
   }
-
-  // (xb) class name strings
-  // (xb) method name strings
-  // (xb) source file strings
-  JDWP::Set4BE(&bytes[string_table_offset], bytes.size());
-  class_names.WriteTo(bytes);
-  method_names.WriteTo(bytes);
-  filenames.WriteTo(bytes);
-
   JNIEnv* env = self->GetJniEnv();
   jbyteArray result = env->NewByteArray(bytes.size());
   if (result != NULL) {