Merge "Layout code items of post startup methods"
diff --git a/build/Android.common_path.mk b/build/Android.common_path.mk
index 4466118..f4f8d49 100644
--- a/build/Android.common_path.mk
+++ b/build/Android.common_path.mk
@@ -74,9 +74,6 @@
 TARGET_CORE_IMG_LOCATION := $(ART_TARGET_TEST_OUT)/core.art
 
 # Jar files for core.art.
-TARGET_CORE_JARS := core-oj core-libart conscrypt okhttp bouncycastle apache-xml
-HOST_CORE_JARS := $(addsuffix -hostdex,$(TARGET_CORE_JARS))
-
 HOST_CORE_DEX_LOCATIONS   := $(foreach jar,$(HOST_CORE_JARS),  $(HOST_OUT_JAVA_LIBRARIES)/$(jar).jar)
 ifeq ($(ART_TEST_ANDROID_ROOT),)
 TARGET_CORE_DEX_LOCATIONS := $(foreach jar,$(TARGET_CORE_JARS),/$(DEXPREOPT_BOOT_JAR_DIR)/$(jar).jar)
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 62ee445..a200d8d 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -690,10 +690,10 @@
 }
 
 MethodItem* Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii) {
-  MethodId* method_item = GetMethodId(cdii.GetMemberIndex());
+  MethodId* method_id = GetMethodId(cdii.GetMemberIndex());
   uint32_t access_flags = cdii.GetRawMemberAccessFlags();
   const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem();
-  CodeItem* code_item = code_items_.GetExistingObject(cdii.GetMethodCodeItemOffset());;
+  CodeItem* code_item = code_items_.GetExistingObject(cdii.GetMethodCodeItemOffset());
   DebugInfoItem* debug_info = nullptr;
   if (disk_code_item != nullptr) {
     if (code_item == nullptr) {
@@ -707,7 +707,7 @@
         disk_code_item, is_static, cdii.GetMemberIndex(), GetLocalsCb, debug_info);
     dex_file.DecodeDebugPositionInfo(disk_code_item, GetPositionsCb, debug_info);
   }
-  return new MethodItem(access_flags, method_item, code_item);
+  return new MethodItem(access_flags, method_id, code_item);
 }
 
 ClassData* Collections::CreateClassData(
@@ -719,14 +719,14 @@
     ClassDataItemIterator cdii(dex_file, encoded_data);
     // Static fields.
     FieldItemVector* static_fields = new FieldItemVector();
-    for (uint32_t i = 0; cdii.HasNextStaticField(); i++, cdii.Next()) {
+    for (; cdii.HasNextStaticField(); cdii.Next()) {
       FieldId* field_item = GetFieldId(cdii.GetMemberIndex());
       uint32_t access_flags = cdii.GetRawMemberAccessFlags();
       static_fields->push_back(std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
     }
     // Instance fields.
     FieldItemVector* instance_fields = new FieldItemVector();
-    for (uint32_t i = 0; cdii.HasNextInstanceField(); i++, cdii.Next()) {
+    for (; cdii.HasNextInstanceField(); cdii.Next()) {
       FieldId* field_item = GetFieldId(cdii.GetMemberIndex());
       uint32_t access_flags = cdii.GetRawMemberAccessFlags();
       instance_fields->push_back(
@@ -734,15 +734,13 @@
     }
     // Direct methods.
     MethodItemVector* direct_methods = new MethodItemVector();
-    for (uint32_t i = 0; cdii.HasNextDirectMethod(); i++, cdii.Next()) {
-      direct_methods->push_back(
-          std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
+    for (; cdii.HasNextDirectMethod(); cdii.Next()) {
+      direct_methods->push_back(std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
     }
     // Virtual methods.
     MethodItemVector* virtual_methods = new MethodItemVector();
-    for (uint32_t i = 0; cdii.HasNextVirtualMethod(); i++, cdii.Next()) {
-      virtual_methods->push_back(
-          std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
+    for (; cdii.HasNextVirtualMethod(); cdii.Next()) {
+      virtual_methods->push_back(std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
     }
     class_data = new ClassData(static_fields, instance_fields, direct_methods, virtual_methods);
     class_data->SetSize(cdii.EndDataPointer() - encoded_data);
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 95e64bf..fe74572 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -215,14 +215,38 @@
       const DexFile& dex_file, const DexFile::CodeItem& disk_code_item, uint32_t offset);
   ClassData* CreateClassData(const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset);
 
-  StringId* GetStringId(uint32_t index) { return StringIds()[index].get(); }
-  TypeId* GetTypeId(uint32_t index) { return TypeIds()[index].get(); }
-  ProtoId* GetProtoId(uint32_t index) { return ProtoIds()[index].get(); }
-  FieldId* GetFieldId(uint32_t index) { return FieldIds()[index].get(); }
-  MethodId* GetMethodId(uint32_t index) { return MethodIds()[index].get(); }
-  ClassDef* GetClassDef(uint32_t index) { return ClassDefs()[index].get(); }
-  CallSiteId* GetCallSiteId(uint32_t index) { return CallSiteIds()[index].get(); }
-  MethodHandleItem* GetMethodHandle(uint32_t index) { return MethodHandleItems()[index].get(); }
+  StringId* GetStringId(uint32_t index) {
+    CHECK_LT(index, StringIdsSize());
+    return StringIds()[index].get();
+  }
+  TypeId* GetTypeId(uint32_t index) {
+    CHECK_LT(index, TypeIdsSize());
+    return TypeIds()[index].get();
+  }
+  ProtoId* GetProtoId(uint32_t index) {
+    CHECK_LT(index, ProtoIdsSize());
+    return ProtoIds()[index].get();
+  }
+  FieldId* GetFieldId(uint32_t index) {
+    CHECK_LT(index, FieldIdsSize());
+    return FieldIds()[index].get();
+  }
+  MethodId* GetMethodId(uint32_t index) {
+    CHECK_LT(index, MethodIdsSize());
+    return MethodIds()[index].get();
+  }
+  ClassDef* GetClassDef(uint32_t index) {
+    CHECK_LT(index, ClassDefsSize());
+    return ClassDefs()[index].get();
+  }
+  CallSiteId* GetCallSiteId(uint32_t index) {
+    CHECK_LT(index, CallSiteIdsSize());
+    return CallSiteIds()[index].get();
+  }
+  MethodHandleItem* GetMethodHandle(uint32_t index) {
+    CHECK_LT(index, MethodHandleItemsSize());
+    return MethodHandleItems()[index].get();
+  }
 
   StringId* GetStringIdOrNullPtr(uint32_t index) {
     return index == DexFile::kDexNoIndex ? nullptr : GetStringId(index);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 7e70b75..12bdb32 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -2927,7 +2927,8 @@
 
 void Dbg::PostFieldAccessEvent(ArtMethod* m, int dex_pc,
                                mirror::Object* this_object, ArtField* f) {
-  if (!IsDebuggerActive()) {
+  // TODO We should send events for native methods.
+  if (!IsDebuggerActive() || m->IsNative()) {
     return;
   }
   DCHECK(m != nullptr);
@@ -2941,7 +2942,8 @@
 void Dbg::PostFieldModificationEvent(ArtMethod* m, int dex_pc,
                                      mirror::Object* this_object, ArtField* f,
                                      const JValue* field_value) {
-  if (!IsDebuggerActive()) {
+  // TODO We should send events for native methods.
+  if (!IsDebuggerActive() || m->IsNative()) {
     return;
   }
   DCHECK(m != nullptr);
diff --git a/runtime/dex_file_tracking_registrar.cc b/runtime/dex_file_tracking_registrar.cc
index f41a50b..848e2f3 100644
--- a/runtime/dex_file_tracking_registrar.cc
+++ b/runtime/dex_file_tracking_registrar.cc
@@ -69,7 +69,10 @@
 
 // Intended for local changes only.
 void DexFileTrackingRegistrar::SetDexSections() {
-  if (kDexFileAccessTracking || dex_file_ != nullptr) {
+  if (kDexFileAccessTracking && dex_file_ != nullptr) {
+    // Logs the Dex File's location and starting address if tracking is enabled
+    LOG(ERROR) << "RegisterDexFile: " << dex_file_->GetLocation() + " @ " << std::hex
+               << reinterpret_cast<uintptr_t>(dex_file_->Begin());
     switch (kCurrentTrackingSystem) {
       case kWholeDexTracking:
         SetDexFileRegistration(true);
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index 6be0953..0aabbea 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -40,6 +40,7 @@
 #include "interpreter/interpreter.h"
 #include "jni_env_ext.h"
 #include "java_vm_ext.h"
+#include "jvalue-inl.h"
 #include "mirror/class-inl.h"
 #include "mirror/class_loader.h"
 #include "mirror/field-inl.h"
@@ -64,6 +65,84 @@
 // things not rendering correctly. E.g. b/16858794
 static constexpr bool kWarnJniAbort = false;
 
+// Helpers to call instrumentation functions for fields. These take jobjects so we don't need to set
+// up handles for the rare case where these actually do something. Once these functions return it is
+// possible there will be a pending exception if the instrumentation happens to throw one.
+static void NotifySetObjectField(ArtField* field, jobject obj, jobject jval)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK_EQ(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
+  instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+  if (UNLIKELY(instrumentation->HasFieldWriteListeners())) {
+    Thread* self = Thread::Current();
+    ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
+                                                   /*check_suspended*/ true,
+                                                   /*abort_on_error*/ false);
+
+    if (cur_method == nullptr) {
+      // Set/Get Fields can be issued without a method during runtime startup/teardown. Ignore all
+      // of these changes.
+      return;
+    }
+    DCHECK(cur_method->IsNative());
+    JValue val;
+    val.SetL(self->DecodeJObject(jval));
+    instrumentation->FieldWriteEvent(self,
+                                     self->DecodeJObject(obj).Ptr(),
+                                     cur_method,
+                                     0,  // dex_pc is always 0 since this is a native method.
+                                     field,
+                                     val);
+  }
+}
+
+static void NotifySetPrimitiveField(ArtField* field, jobject obj, JValue val)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  DCHECK_NE(field->GetTypeAsPrimitiveType(), Primitive::kPrimNot);
+  instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+  if (UNLIKELY(instrumentation->HasFieldWriteListeners())) {
+    Thread* self = Thread::Current();
+    ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
+                                                   /*check_suspended*/ true,
+                                                   /*abort_on_error*/ false);
+
+    if (cur_method == nullptr) {
+      // Set/Get Fields can be issued without a method during runtime startup/teardown. Ignore all
+      // of these changes.
+      return;
+    }
+    DCHECK(cur_method->IsNative());
+    instrumentation->FieldWriteEvent(self,
+                                     self->DecodeJObject(obj).Ptr(),
+                                     cur_method,
+                                     0,  // dex_pc is always 0 since this is a native method.
+                                     field,
+                                     val);
+  }
+}
+
+static void NotifyGetField(ArtField* field, jobject obj)
+    REQUIRES_SHARED(Locks::mutator_lock_) {
+  instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation();
+  if (UNLIKELY(instrumentation->HasFieldReadListeners())) {
+    Thread* self = Thread::Current();
+    ArtMethod* cur_method = self->GetCurrentMethod(/*dex_pc*/ nullptr,
+                                                   /*check_suspended*/ true,
+                                                   /*abort_on_error*/ false);
+
+    if (cur_method == nullptr) {
+      // Set/Get Fields can be issued without a method during runtime startup/teardown. Ignore all
+      // of these changes.
+      return;
+    }
+    DCHECK(cur_method->IsNative());
+    instrumentation->FieldReadEvent(self,
+                                    self->DecodeJObject(obj).Ptr(),
+                                    cur_method,
+                                    0,  // dex_pc is always 0 since this is a native method.
+                                    field);
+  }
+}
+
 // Section 12.3.2 of the JNI spec describes JNI class descriptors. They're
 // separated with slashes but aren't wrapped with "L;" like regular descriptors
 // (i.e. "a/b/C" rather than "La/b/C;"). Arrays of reference types are an
@@ -1235,8 +1314,9 @@
     CHECK_NON_NULL_ARGUMENT(obj);
     CHECK_NON_NULL_ARGUMENT(fid);
     ScopedObjectAccess soa(env);
-    ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(obj);
     ArtField* f = jni::DecodeArtField(fid);
+    NotifyGetField(f, obj);
+    ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(obj);
     return soa.AddLocalReference<jobject>(f->GetObject(o));
   }
 
@@ -1244,6 +1324,7 @@
     CHECK_NON_NULL_ARGUMENT(fid);
     ScopedObjectAccess soa(env);
     ArtField* f = jni::DecodeArtField(fid);
+    NotifyGetField(f, nullptr);
     return soa.AddLocalReference<jobject>(f->GetObject(f->GetDeclaringClass()));
   }
 
@@ -1251,17 +1332,19 @@
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(java_object);
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid);
     ScopedObjectAccess soa(env);
+    ArtField* f = jni::DecodeArtField(fid);
+    NotifySetObjectField(f, java_object, java_value);
     ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(java_object);
     ObjPtr<mirror::Object> v = soa.Decode<mirror::Object>(java_value);
-    ArtField* f = jni::DecodeArtField(fid);
     f->SetObject<false>(o, v);
   }
 
   static void SetStaticObjectField(JNIEnv* env, jclass, jfieldID fid, jobject java_value) {
     CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid);
     ScopedObjectAccess soa(env);
-    ObjPtr<mirror::Object> v = soa.Decode<mirror::Object>(java_value);
     ArtField* f = jni::DecodeArtField(fid);
+    NotifySetObjectField(f, nullptr, java_value);
+    ObjPtr<mirror::Object> v = soa.Decode<mirror::Object>(java_value);
     f->SetObject<false>(f->GetDeclaringClass(), v);
   }
 
@@ -1269,28 +1352,32 @@
   CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(instance); \
   CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \
   ScopedObjectAccess soa(env); \
-  ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \
   ArtField* f = jni::DecodeArtField(fid); \
+  NotifyGetField(f, instance); \
+  ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \
   return f->Get ##fn (o)
 
 #define GET_STATIC_PRIMITIVE_FIELD(fn) \
   CHECK_NON_NULL_ARGUMENT_RETURN_ZERO(fid); \
   ScopedObjectAccess soa(env); \
   ArtField* f = jni::DecodeArtField(fid); \
+  NotifyGetField(f, nullptr); \
   return f->Get ##fn (f->GetDeclaringClass())
 
 #define SET_PRIMITIVE_FIELD(fn, instance, value) \
   CHECK_NON_NULL_ARGUMENT_RETURN_VOID(instance); \
   CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \
   ScopedObjectAccess soa(env); \
-  ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \
   ArtField* f = jni::DecodeArtField(fid); \
+  NotifySetPrimitiveField(f, instance, JValue::FromPrimitive<decltype(value)>(value)); \
+  ObjPtr<mirror::Object> o = soa.Decode<mirror::Object>(instance); \
   f->Set ##fn <false>(o, value)
 
 #define SET_STATIC_PRIMITIVE_FIELD(fn, value) \
   CHECK_NON_NULL_ARGUMENT_RETURN_VOID(fid); \
   ScopedObjectAccess soa(env); \
   ArtField* f = jni::DecodeArtField(fid); \
+  NotifySetPrimitiveField(f, nullptr, JValue::FromPrimitive<decltype(value)>(value)); \
   f->Set ##fn <false>(f->GetDeclaringClass(), value)
 
   static jboolean GetBooleanField(JNIEnv* env, jobject obj, jfieldID fid) {
diff --git a/runtime/jvalue-inl.h b/runtime/jvalue-inl.h
index b33686c..25e34b2 100644
--- a/runtime/jvalue-inl.h
+++ b/runtime/jvalue-inl.h
@@ -27,6 +27,24 @@
   l = new_l.Ptr();
 }
 
+#define DEFINE_FROM(type, chr) \
+    template <> inline JValue JValue::FromPrimitive(type v) { \
+      JValue res; \
+      res.Set ## chr(v); \
+      return res; \
+    }
+
+DEFINE_FROM(uint8_t, Z);
+DEFINE_FROM(int8_t, B);
+DEFINE_FROM(uint16_t, C);
+DEFINE_FROM(int16_t, S);
+DEFINE_FROM(int32_t, I);
+DEFINE_FROM(int64_t, J);
+DEFINE_FROM(float, F);
+DEFINE_FROM(double, D);
+
+#undef DEFINE_FROM
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_JVALUE_INL_H_
diff --git a/runtime/jvalue.h b/runtime/jvalue.h
index f61a07c..266abcf 100644
--- a/runtime/jvalue.h
+++ b/runtime/jvalue.h
@@ -33,6 +33,8 @@
   // We default initialize JValue instances to all-zeros.
   JValue() : j(0) {}
 
+  template<typename T> static JValue FromPrimitive(T v);
+
   int8_t GetB() const { return b; }
   void SetB(int8_t new_b) {
     j = ((static_cast<int64_t>(new_b) << 56) >> 56);  // Sign-extend to 64 bits.
diff --git a/runtime/openjdkjvmti/ti_class.cc b/runtime/openjdkjvmti/ti_class.cc
index cd078b6..3321814 100644
--- a/runtime/openjdkjvmti/ti_class.cc
+++ b/runtime/openjdkjvmti/ti_class.cc
@@ -313,8 +313,10 @@
       art::Thread* thread = art::Thread::Current();
       ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
                                     thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
+      art::ObjPtr<art::mirror::Object> peer(thread->GetPeer());
       ScopedLocalRef<jthread> thread_jni(
-          thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer()));
+          thread->GetJniEnv(),
+          peer.IsNull() ? nullptr : thread->GetJniEnv()->AddLocalReference<jthread>(peer));
       {
         art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
         event_handler->DispatchEvent<ArtJvmtiEvent::kClassLoad>(
@@ -341,8 +343,10 @@
       }
       ScopedLocalRef<jclass> jklass(thread->GetJniEnv(),
                                     thread->GetJniEnv()->AddLocalReference<jclass>(klass.Get()));
+      art::ObjPtr<art::mirror::Object> peer(thread->GetPeer());
       ScopedLocalRef<jthread> thread_jni(
-          thread->GetJniEnv(), thread->GetJniEnv()->AddLocalReference<jthread>(thread->GetPeer()));
+          thread->GetJniEnv(),
+          peer.IsNull() ? nullptr : thread->GetJniEnv()->AddLocalReference<jthread>(peer));
       art::ScopedThreadSuspension sts(thread, art::ThreadState::kNative);
       event_handler->DispatchEvent<ArtJvmtiEvent::kClassPrepare>(
           thread,
diff --git a/test/044-proxy/check b/test/044-proxy/check
new file mode 100755
index 0000000..0cb2c32
--- /dev/null
+++ b/test/044-proxy/check
@@ -0,0 +1,33 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+expected="$1"
+output="$2"
+
+# Jack inserts a synthetic default method for interface methods with covariant return types.
+# Javac does not do this, so we must fiddle with the expected.txt to get the different behavior.
+
+expected_jack_line='Invocation of public default java[.]lang[.]Object NarrowingTest\$I2[.]foo[(][)]'
+replaced_javac_line='Invocation of public abstract java.lang.Object NarrowingTest\$I1.foo()'
+
+expected_replaced="$expected"
+
+if [[ $USE_JACK == false ]]; then
+  expected_replaced="$output.tmp"
+  sed "s:$expected_jack_line:$replaced_javac_line:g" "$expected" > "$expected_replaced"
+fi
+
+diff --strip-trailing-cr -q "$expected_replaced" "$output" >/dev/null
diff --git a/test/104-growth-limit/src/Main.java b/test/104-growth-limit/src/Main.java
index d31cbf1..24acb91 100644
--- a/test/104-growth-limit/src/Main.java
+++ b/test/104-growth-limit/src/Main.java
@@ -21,7 +21,6 @@
 public class Main {
 
     public static void main(String[] args) throws Exception {
-        int alloc1 = 1;
         // Setup reflection stuff before allocating to prevent OOME caused by allocations from
         // Class.forName or getDeclaredMethod.
         // Reflective equivalent of: dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
@@ -29,34 +28,38 @@
         final Method get_runtime = vm_runtime.getDeclaredMethod("getRuntime");
         final Object runtime = get_runtime.invoke(null);
         final Method clear_growth_limit = vm_runtime.getDeclaredMethod("clearGrowthLimit");
-        List<byte[]> l = new ArrayList<byte[]>();
-        try {
-            while (true) {
-                // Allocate a MB at a time
-                l.add(new byte[1048576]);
-                alloc1++;
-            }
-        } catch (OutOfMemoryError e) {
-            l = null;
-        }
+
+        int alloc1 = allocateTillOOME();
+
+        // Proactively clean up.
+        Runtime.getRuntime().gc();
+
         // Expand the heap to the maximum size.
         clear_growth_limit.invoke(runtime);
-        int alloc2 = 1;
-        l = new ArrayList<byte[]>();
-        try {
-            while (true) {
-                // Allocate a MB at a time
-                l.add(new byte[1048576]);
-                alloc2++;
-            }
-        } catch (OutOfMemoryError e2) {
-            l = null;
-            if (alloc1 > alloc2) {
-                System.out.println("ERROR: Allocated less memory after growth" +
+
+        int alloc2 = allocateTillOOME();
+
+        if (alloc1 > alloc2) {
+            System.out.println("ERROR: Allocated less memory after growth" +
                     "limit cleared (" + alloc1 + " MBs > " + alloc2 + " MBs");
-                System.exit(1);
-            }
+        } else {
+            System.out.println("Test complete");
         }
-        System.out.println("Test complete");
+    }
+
+    private static int allocateTillOOME() {
+      int allocations = 0;
+      List<byte[]> l = new ArrayList<byte[]>();
+      try {
+          while (true) {
+              // Allocate a MB at a time
+              l.add(new byte[1048576]);
+              allocations++;
+          }
+      } catch (OutOfMemoryError e) {
+          // Help clean up.
+          l.clear();
+      }
+      return allocations;
     }
 }
diff --git a/test/551-checker-shifter-operand/build b/test/551-checker-shifter-operand/build
deleted file mode 100644
index 027a0ea..0000000
--- a/test/551-checker-shifter-operand/build
+++ /dev/null
@@ -1,212 +0,0 @@
-#!/bin/bash
-#
-# 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.
-
-
-# This is an almost exact copy of `art/test/etc/default-build`. Only the parsing
-# of `dx` option has been overriden.
-
-# Stop if something fails.
-set -e
-
-# Set default values for directories.
-if [ -d smali ]; then
-  HAS_SMALI=true
-else
-  HAS_SMALI=false
-fi
-
-if [ -d src ]; then
-  HAS_SRC=true
-else
-  HAS_SRC=false
-fi
-
-if [ -d src2 ]; then
-  HAS_SRC2=true
-else
-  HAS_SRC2=false
-fi
-
-if [ -d src-multidex ]; then
-  HAS_SRC_MULTIDEX=true
-else
-  HAS_SRC_MULTIDEX=false
-fi
-
-if [ -d src-ex ]; then
-  HAS_SRC_EX=true
-else
-  HAS_SRC_EX=false
-fi
-
-DX_FLAGS=""
-SKIP_DX_MERGER="false"
-EXPERIMENTAL=""
-
-# Setup experimental flag mappings in a bash associative array.
-declare -A JACK_EXPERIMENTAL_ARGS
-JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
-JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24"
-
-while true; do
-  if [ "x$1" = "x--dx-option" ]; then
-    shift
-    option="$1"
-    # Make sure we run this test *with* `dx` optimizations.
-    if [ "x$option" != "x--no-optimize" ]; then
-      DX_FLAGS="${DX_FLAGS} $option"
-    fi
-    shift
-  elif [ "x$1" = "x--jvm" ]; then
-    shift
-  elif [ "x$1" = "x--no-src" ]; then
-    HAS_SRC=false
-    shift
-  elif [ "x$1" = "x--no-src2" ]; then
-    HAS_SRC2=false
-    shift
-  elif [ "x$1" = "x--no-src-multidex" ]; then
-    HAS_SRC_MULTIDEX=false
-    shift
-  elif [ "x$1" = "x--no-src-ex" ]; then
-    HAS_SRC_EX=false
-    shift
-  elif [ "x$1" = "x--no-smali" ]; then
-    HAS_SMALI=false
-    shift
-  elif [ "x$1" = "x--experimental" ]; then
-    shift
-    EXPERIMENTAL="${EXPERIMENTAL} $1"
-    shift
-  elif expr "x$1" : "x--" >/dev/null 2>&1; then
-    echo "unknown $0 option: $1" 1>&2
-    exit 1
-  else
-    break
-  fi
-done
-
-# Add args from the experimental mappings.
-for experiment in ${EXPERIMENTAL}; do
-  JACK_ARGS="${JACK_ARGS} ${JACK_EXPERIMENTAL_ARGS[${experiment}]}"
-done
-
-if [ -e classes.dex ]; then
-  zip $TEST_NAME.jar classes.dex
-  exit 0
-fi
-
-if ! [ "${HAS_SRC}" = "true" ] && ! [ "${HAS_SRC2}" = "true" ]; then
-  # No src directory? Then forget about trying to run dx.
-  SKIP_DX_MERGER="true"
-fi
-
-if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
-  # Jack does not support this configuration unless we specify how to partition the DEX file
-  # with a .jpp file.
-  USE_JACK="false"
-fi
-
-if [ ${USE_JACK} = "true" ]; then
-  # Jack toolchain
-  if [ "${HAS_SRC}" = "true" ]; then
-    ${JACK} ${JACK_ARGS} --output-jack src.jack src
-    imported_jack_files="--import src.jack"
-  fi
-
-  if [ "${HAS_SRC2}" = "true" ]; then
-    ${JACK} ${JACK_ARGS} --output-jack src2.jack src2
-    imported_jack_files="--import src2.jack ${imported_jack_files}"
-  fi
-
-  # Compile jack files into a DEX file. We set jack.import.type.policy=keep-first to consider
-  # class definitions from src2 first.
-  if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then
-    ${JACK} ${JACK_ARGS} ${imported_jack_files} -D jack.import.type.policy=keep-first --output-dex .
-  fi
-else
-  # Legacy toolchain with javac+dx
-  if [ "${HAS_SRC}" = "true" ]; then
-    mkdir classes
-    ${JAVAC} ${JAVAC_ARGS} -implicit:none -classpath src-multidex -d classes `find src -name '*.java'`
-  fi
-
-  if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
-    mkdir classes2
-    ${JAVAC} -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'`
-    if [ ${NEED_DEX} = "true" ]; then
-      ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex \
-        --dump-width=1000 ${DX_FLAGS} classes2
-    fi
-  fi
-
-  if [ "${HAS_SRC2}" = "true" ]; then
-    mkdir -p classes
-    ${JAVAC} ${JAVAC_ARGS} -d classes `find src2 -name '*.java'`
-  fi
-
-  if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then
-    if [ ${NEED_DEX} = "true" -a ${SKIP_DX_MERGER} = "false" ]; then
-      ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
-        --dump-width=1000 ${DX_FLAGS} classes
-    fi
-  fi
-fi
-
-if [ "${HAS_SMALI}" = "true" ]; then
-  # Compile Smali classes
-  ${SMALI} -JXmx512m assemble ${SMALI_ARGS} --output smali_classes.dex `find smali -name '*.smali'`
-
-  # Don't bother with dexmerger if we provide our own main function in a smali file.
-  if [ ${SKIP_DX_MERGER} = "false" ]; then
-    ${DXMERGER} classes.dex classes.dex smali_classes.dex
-  else
-    mv smali_classes.dex classes.dex
-  fi
-fi
-
-if [ ${HAS_SRC_EX} = "true" ]; then
-  if [ ${USE_JACK} = "true" ]; then
-      # Rename previous "classes.dex" so it is not overwritten.
-      mv classes.dex classes-1.dex
-      #TODO find another way to append src.jack to the jack classpath
-      ${JACK}:src.jack ${JACK_ARGS} --output-dex . src-ex
-      zip $TEST_NAME-ex.jar classes.dex
-      # Restore previous "classes.dex" so it can be zipped.
-      mv classes-1.dex classes.dex
-  else
-    mkdir classes-ex
-    ${JAVAC} ${JAVAC_ARGS} -d classes-ex -cp classes `find src-ex -name '*.java'`
-    if [ ${NEED_DEX} = "true" ]; then
-      ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes-ex.dex \
-        --dump-width=1000 ${DX_FLAGS} classes-ex
-
-      # quick shuffle so that the stored name is "classes.dex"
-      mv classes.dex classes-1.dex
-      mv classes-ex.dex classes.dex
-      zip $TEST_NAME-ex.jar classes.dex
-      mv classes.dex classes-ex.dex
-      mv classes-1.dex classes.dex
-    fi
-  fi
-fi
-
-# Create a single jar with two dex files for multidex.
-if [ ${HAS_SRC_MULTIDEX} = "true" ]; then
-  zip $TEST_NAME.jar classes.dex classes2.dex
-elif [ ${NEED_DEX} = "true" ]; then
-  zip $TEST_NAME.jar classes.dex
-fi
diff --git a/test/636-arm64-veneer-pool/build b/test/636-arm64-veneer-pool/build
index 27cc4d6..eba22fc 100755
--- a/test/636-arm64-veneer-pool/build
+++ b/test/636-arm64-veneer-pool/build
@@ -19,4 +19,9 @@
 
 # Use javac+dx instead of jack.
 export USE_JACK=false
-./default-build "$@"
+
+# Don't use desugar because the bootclasspath jars will be missing
+# on a platform build compiled with ANDROID_COMPILE_WITH_JACK=true.
+export USE_DESUGAR=false
+
+./default-build
diff --git a/test/641-checker-arraycopy/build b/test/641-checker-arraycopy/build
index 9abc618..12e4423 100644
--- a/test/641-checker-arraycopy/build
+++ b/test/641-checker-arraycopy/build
@@ -21,4 +21,8 @@
 # the typed System.arraycopy versions directly.
 export USE_JACK=false
 
-./default-build
+# Don't use desugar because the bootclasspath jars will be missing
+# on a platform build compiled with ANDROID_COMPILE_WITH_JACK=true.
+export USE_DESUGAR=false
+
+./default-build "$@"
diff --git a/test/etc/default-build b/test/etc/default-build
index 0508b85..ba413ec 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -60,6 +60,11 @@
   HAS_SRC_DEX2OAT_UNRESOLVED=false
 fi
 
+# DESUGAR=false run-test... will disable desugar.
+if [[ "$DESUGAR" == false ]]; then
+  USE_DESUGAR=false
+fi
+
 # Allow overriding ZIP_COMPRESSION_METHOD with e.g. 'store'
 ZIP_COMPRESSION_METHOD="deflate"
 # Align every ZIP file made by calling $ZIPALIGN command?
@@ -70,6 +75,9 @@
 SKIP_DX_MERGER="false"
 EXPERIMENTAL=""
 
+BUILD_MODE="target"
+DEV_MODE="no"
+
 # The key for default arguments if no experimental things are enabled.
 DEFAULT_EXPERIMENT="no-experiment"
 
@@ -137,6 +145,15 @@
     WITH_ZIP_ALIGN=true
     ZIP_ALIGN_BYTES="$1"
     shift
+  elif [ "x$1" = "x--host" ]; then
+    BUILD_MODE="host"
+    shift
+  elif [ "x$1" = "x--target" ]; then
+    BUILD_MODE="target"
+    shift
+  elif [ "x$1" = "x--dev" ]; then
+    DEV_MODE="yes"
+    shift
   elif expr "x$1" : "x--" >/dev/null 2>&1; then
     echo "unknown $0 option: $1" 1>&2
     exit 1
@@ -185,6 +202,41 @@
   fi
 }
 
+function desugar() {
+  local desugar_args=--mode=host
+  if [[ $BUILD_MODE == target ]]; then
+    desugar_args=--mode=target
+  fi
+
+  if [[ $DEV_MODE == yes ]]; then
+    desugar_args="$desugar_args --show-commands"
+  fi
+
+  "$DESUGAR" --core-only $desugar_args "$@"
+}
+
+# Make a "dex" file given a directory of classes in $1.
+# Also calls desugar on the classes first to convert lambdas.
+function make_dex() {
+  local name="$1"
+
+  local dx_input
+  if [[ "$USE_DESUGAR" == "true" ]]; then
+    # Make a jar first so desugar doesn't need every .class file individually.
+    jar cf "$name.before-desugar.jar" -C "$name" .
+
+    dx_input="${name}.desugar.jar"
+
+    # Make desugared JAR.
+    desugar --input "$name.before-desugar.jar" --output "$dx_input"
+  else
+    dx_input="${name}"
+  fi
+
+  # Make dex file from desugared JAR.
+  ${DX} -JXmx256m --debug --dex --dump-to=${name}.lst --output=${name}.dex --dump-width=1000 ${DX_FLAGS} "${dx_input}"
+}
+
 if [ -e classes.dex ]; then
   zip $TEST_NAME.jar classes.dex
   exit 0
@@ -209,9 +261,9 @@
     ${JACK} --import classes.jill.jar --output-dex .
   else
     if [ ${NEED_DEX} = "true" ]; then
-      ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 ${DX_FLAGS} classes-ex
+      make_dex classes-ex
       zip ${TEST_NAME}-ex.jar classes.dex
-      ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 ${DX_FLAGS} classes
+      make_dex classes
     fi
   fi
 else
@@ -254,8 +306,7 @@
       mkdir classes2
       ${JAVAC} -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'`
       if [ ${NEED_DEX} = "true" ]; then
-        ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex \
-          --dump-width=1000 ${DX_FLAGS} classes2
+        make_dex classes2
       fi
     fi
 
@@ -266,8 +317,7 @@
 
     if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then
       if [ ${NEED_DEX} = "true" -a ${SKIP_DX_MERGER} = "false" ]; then
-        ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
-          --dump-width=1000 ${DX_FLAGS} classes
+        make_dex classes
       fi
     fi
   fi
@@ -311,8 +361,7 @@
     mkdir classes-ex
     ${JAVAC} ${JAVAC_ARGS} -d classes-ex -cp classes `find src-ex -name '*.java'`
     if [ ${NEED_DEX} = "true" ]; then
-      ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes-ex.dex \
-        --dump-width=1000 ${DX_FLAGS} classes-ex
+      make_dex classes-ex
 
       # quick shuffle so that the stored name is "classes.dex"
       mv classes.dex classes-1.dex
diff --git a/test/knownfailures.json b/test/knownfailures.json
index b89d457..b7f5d4c 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -601,38 +601,28 @@
             "004-ReferenceMap",
             "004-StackWalk",
             "048-reflect-v8",
+            "065-mismatched-implements",
             "089-many-methods",
             "138-duplicate-classes-check",
             "146-bad-interface",
             "157-void-class",
+            "529-checker-unresolved",
             "563-checker-invoke-super",
             "580-checker-string-fact-intrinsics",
             "596-monitor-inflation",
             "604-hot-static-interface",
+            "608-checker-unresolved-lse",
             "612-jit-dex-cache",
             "613-inlining-dex-cache",
             "616-cha-interface-default",
             "636-wrong-static-access",
+            "648-inline-caches-unresolved",
             "909-attach-agent",
             "910-methods",
             "911-get-stack-trace",
             "912-classes",
             "913-heaps",
-            "914-hello-obsolescence",
-            "915-obsolete-2",
-            "916-obsolete-jit",
-            "919-obsolete-fields",
-            "921-hello-failure",
-            "926-multi-obsolescence",
-            "940-recursive-obsolete",
-            "941-recurive-obsolete-jit",
-            "942-private-recursive",
-            "943-private-recursive-jit",
-            "945-obsolete-native",
-            "946-obsolete-throw",
             "948-change-annotations",
-            "950-redefine-intrinsic",
-            "951-threaded-obsolete",
             "952-invoke-custom",
             "953-invoke-polymorphic-compiler",
             "956-methodhandles",
@@ -648,9 +638,7 @@
             "966-default-conflict",
             "967-default-ame",
             "969-iface-super",
-            "981-dedup-original-dex",
-            "984-obsolete-invoke",
-            "985-re-obsolete"
+            "981-dedup-original-dex"
         ],
         "description": "The tests above fail with --build-with-javac-dx.",
         "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"},
diff --git a/test/run-test b/test/run-test
index 41a0dc2..1b6df16 100755
--- a/test/run-test
+++ b/test/run-test
@@ -46,6 +46,7 @@
 export DEX_LOCATION=/data/run-test/${test_dir}
 export NEED_DEX="true"
 export USE_JACK="true"
+export USE_DESUGAR="true"
 export SMALI_ARGS=""
 
 # If dx was not set by the environment variable, assume it is in the path.
@@ -90,6 +91,11 @@
 
 export JACK="$JACK -g -cp $JACK_CLASSPATH"
 
+# Allow changing DESUGAR script to something else, or to disable it with DESUGAR=false.
+if [ -z "$DESUGAR"]; then
+  export DESUGAR="$ANDROID_BUILD_TOP/art/tools/desugar.sh"
+fi
+
 # Zipalign is not on the PATH in some configs, auto-detect it.
 if [ -z "$ZIPALIGN" ]; then
   if which zipalign >/dev/null; then
@@ -771,6 +777,16 @@
   err_echo "ulimit file size setting failed"
 fi
 
+if [[ "$target_mode" == "yes" ]]; then
+  build_args="$build_args --target"
+else
+  build_args="$build_args --host"
+fi
+
+if [[ "$dev_mode" == "yes" ]]; then
+  build_args="$build_args --dev"
+fi
+
 good="no"
 good_build="yes"
 good_run="yes"
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index 515a391..76f8943 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -338,6 +338,20 @@
   LOG(INFO) << "Entering method \"" << method_info << "\". Thread is \"" << info.GetName() << "\"";
 }
 
+void JNICALL ClassPrepareHook(jvmtiEnv* jvmtienv,
+                              JNIEnv* env,
+                              jthread thread,
+                              jclass klass) {
+  ScopedThreadInfo info(jvmtienv, env, thread);
+  ScopedClassInfo class_info(jvmtienv, klass);
+  if (!class_info.Init()) {
+    LOG(ERROR) << "Unable to get class info!";
+    return;
+  }
+  LOG(INFO) << "Prepared class \"" << class_info.GetName() << "\". Thread is \""
+            << info.GetName() << "\"";
+}
+
 // The hook we are using.
 void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
                                          JNIEnv* jni_env ATTRIBUTE_UNUSED,
@@ -487,6 +501,7 @@
   cb.VMInit = PerformFinalSetupVMInit;
   cb.MethodEntry = MethodEntryHook;
   cb.MethodExit = MethodExitHook;
+  cb.ClassPrepare = ClassPrepareHook;
   if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
     LOG(ERROR) << "Unable to set class file load hook cb!";
     return 1;
@@ -503,6 +518,14 @@
     LOG(ERROR) << "Unable to enable JVMTI_EVENT_VM_INIT event!";
     return 1;
   }
+  if (data->trace_stress) {
+    if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+                                        JVMTI_EVENT_CLASS_PREPARE,
+                                        nullptr) != JVMTI_ERROR_NONE) {
+      LOG(ERROR) << "Unable to enable CLASS_PREPARE event!";
+      return 1;
+    }
+  }
   if (data->redefine_stress) {
     if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
                                         JVMTI_EVENT_CLASS_FILE_LOAD_HOOK,
diff --git a/tools/add_package_property.sh b/tools/add_package_property.sh
old mode 100644
new mode 100755
diff --git a/tools/bootjars.sh b/tools/bootjars.sh
new file mode 100755
index 0000000..bb47e55
--- /dev/null
+++ b/tools/bootjars.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+#
+# This script lists the boot jars that an ART bootclasspath would need.
+#
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+TOP="$DIR/../.."
+
+source "${TOP}/build/envsetup.sh" >&/dev/null # import get_build_var
+
+selected_env_var=
+core_jars_only=n
+print_file_path=n
+mode=target
+while true; do
+  case $1 in
+    --help)
+      echo "Usage: $0 [--core] [--path] [--host|--target] [--help]"
+      exit 0
+      ;;
+    --core)
+      core_jars_only=y
+      ;;
+    --path)
+      print_file_path=y
+      ;;
+    --host)
+      mode=host
+      ;;
+    --target)
+      mode=target
+      ;;
+    *)
+      break
+      ;;
+  esac
+  shift
+done
+
+if [[ $mode == target ]]; then
+  if [[ $core_jars_only == y ]]; then
+    selected_env_var=TARGET_CORE_JARS
+  else
+    selected_env_var=PRODUCT_BOOT_JARS
+  fi
+  intermediates_env_var=TARGET_OUT_COMMON_INTERMEDIATES
+elif [[ $mode == host ]]; then
+  if [[ $core_jars_only == n ]]; then
+    echo "Error: --host does not have non-core boot jars, --core required" >&2
+    exit 1
+  fi
+  selected_env_var=HOST_CORE_JARS
+  intermediates_env_var=HOST_OUT_COMMON_INTERMEDIATES
+fi
+
+boot_jars_list=$(get_build_var "$selected_env_var")
+
+# Print only the list of boot jars.
+if [[ $print_file_path == n ]]; then
+  echo $boot_jars_list
+  exit 0
+fi
+
+# Print the file path (relative to $TOP) to the classes.jar of each boot jar in the intermediates directory.
+intermediates_dir=$(get_build_var "$intermediates_env_var")
+
+# turn the file path into an absolute path
+intermediates_dir=$(readlink -f $TOP/$intermediates_dir)
+
+for jar in $boot_jars_list; do
+  echo "$intermediates_dir/JAVA_LIBRARIES/${jar}_intermediates/classes.jar"
+done
diff --git a/tools/desugar.sh b/tools/desugar.sh
new file mode 100755
index 0000000..43541aa
--- /dev/null
+++ b/tools/desugar.sh
@@ -0,0 +1,91 @@
+#!/bin/bash
+#
+# Copyright (C) 2017 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.
+
+#
+# Calls desugar.jar with the --bootclasspath_entry values passed in automatically.
+# (This avoids having to manually set a boot class path).
+#
+#
+# Script-specific args:
+#   --mode=[host|target]: Select between host or target bootclasspath (default target).
+#   --core-only:          Use only "core" bootclasspath (e.g. do not include framework).
+#   --show-commands:      Print the desugar command being executed.
+#   --help:               Print above list of args.
+#
+# All other args are forwarded to desugar.jar
+#
+
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+TOP=$DIR/../..
+
+pushd "$TOP" >/dev/null # back to android root.
+
+out=${OUT_DIR:-out}
+desugar_jar=$out/host/linux-x86/framework/desugar.jar
+
+if ! [[ -f $desugar_jar ]]; then
+  echo "Error: Missing $desugar_jar; did you do a build?" >&2
+  exit 1
+fi
+
+desugar_jar=$(readlink -f "$desugar_jar") # absolute path to desugar jar
+popd >/dev/null
+
+bootjars_args=
+mode=target
+showcommands=n
+while true; do
+  case $1 in
+    --help)
+      echo "Usage: $0 [--mode=host|target] [--core-only] [--show-commands] <desugar args>"
+      exit 0
+      ;;
+    --mode=host)
+      bootjars_args="$bootjars_args --host"
+      ;;
+    --mode=target)
+      bootjars_args="$bootjars_args --target"
+      ;;
+    --core-only)
+      bootjars_args="$bootjars_args --core"
+      ;;
+    --show-commands)
+      showcommands=y
+      ;;
+    *)
+      break
+      ;;
+  esac
+  shift
+done
+
+desugar_args=()
+boot_class_path_list=$($TOP/art/tools/bootjars.sh $bootjars_args --path)
+
+for path in $boot_class_path_list; do
+  desugar_args+=(--bootclasspath_entry="$path")
+done
+
+if [[ ${#desugar_args[@]} -eq 0 ]]; then
+  echo "FATAL: Missing bootjars.sh file path list" >&2
+  exit 1
+fi
+
+if [[ $showcommands == y ]]; then
+  echo java -jar "$desugar_jar" "${desugar_args[@]}" "$@"
+fi
+
+java -jar "$desugar_jar" "${desugar_args[@]}" "$@"
diff --git a/tools/runtime_memusage/README b/tools/runtime_memusage/README
new file mode 100644
index 0000000..2543df1
--- /dev/null
+++ b/tools/runtime_memusage/README
@@ -0,0 +1,72 @@
+Dex File Poisoning Access
+=========================
+
+These set of executables are useful for condensing large amounts of memory reads
+of Dex Files into smaller, split pieces of information. Two kinds of information
+are provided:
+	1. Visualizing what part of a Dex File is being accessed at what time
+	as a graph
+	2. Ordering stack traces by most commonly occurring
+Both of these kinds of information can be split up further by providing category
+names as arguments. A trace is put into a category if the category name is a
+substring of the symbolized trace.
+
+How:
+======
+These set of tools  work in conjunction with the class
+DexFileTrackingRegistrar, which marks sections of Dex Files as poisoned. As Dex
+Files are marked for poisoning, their starting addresses are logged in logcat.
+In addition, when poisoned sections of memory are accesses, their stack trace is
+also outputted to logcat.
+
+sanitizer_logcat_analysis.sh is the main executable that will use the other two
+in order to give both types of information. The other two are used in some of
+the intermediary steps which are described in sanitizer_logcat_analysis.sh,
+though they can also be executed individually if provided the necessary input.
+
+Why:
+======
+
+The main reason for splitting the functionality across multiple files is because
+sanitizer_logcat_analysis.sh uses external executable development/scripts/stack.
+This is necessary  in order to get symbolized traces from the output given by
+Address Sanitizer.
+
+How to Use:
+
+sanitizer_logcat_analysis.sh at minimum requires all logcat output in the form
+of a file. Additional options specified below are useful for removing
+unnecessary trace information.
+
+===========================================================================
+Usage: sanitizer_logcat_analysis.sh [options] [LOGCAT_FILE] [CATEGORIES...]
+    -d  OUT_DIRECTORY
+        Puts all output in specified directory.
+        If not given, output will be put in a local
+        temp folder which will be deleted after
+        execution.
+
+    -e
+        All traces will have exactly the same number
+        of categories which is specified by either
+        the -m argument or by prune_sanitizer_output.py
+
+    -f
+        forces redo of all commands even if output
+        files exist.
+
+    -m  [MINIMUM_CALLS_PER_TRACE]
+        Filters out all traces that do not have
+        at least MINIMUM_CALLS_PER_TRACE lines.
+        default: specified by prune_sanitizer_output.py
+
+    CATEGORIES are words that are expected to show in
+       a large subset of symbolized traces. Splits
+       output based on each word.
+
+    LOGCAT_FILE is the piped output from adb logcat.
+===========================================================================
+
+
+
+
diff --git a/tools/runtime_memusage/prune_sanitizer_output.py b/tools/runtime_memusage/prune_sanitizer_output.py
new file mode 100755
index 0000000..222c3c7
--- /dev/null
+++ b/tools/runtime_memusage/prune_sanitizer_output.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 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.
+
+"""Cleans up overlapping portions of traces provided by logcat."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+import argparse
+import os
+import sys
+
+STACK_DIVIDER = 65 * '='
+
+
+def has_min_lines(trace, stack_min_size):
+    """Checks if the trace has a minimum amount of levels in trace."""
+    # Line containing 'use-after-poison' contains address accessed, which is
+    # useful for extracting Dex File offsets
+    string_checks = ['use-after-poison', 'READ']
+    required_checks = string_checks + ['#%d ' % line_ctr
+                                       for line_ctr in
+                                       range(stack_min_size)
+                                       ]
+    try:
+        trace_indices = [trace.index(check) for check in required_checks]
+        return all(trace_indices[trace_ind] < trace_indices[trace_ind + 1]
+                   for trace_ind in range(len(trace_indices) - 1))
+    except ValueError:
+        return False
+    return True
+
+
+def prune_exact(trace, stack_min_size):
+    """Removes all of trace that comes after the (stack_min_size)th trace."""
+    string_checks = ['use-after-poison', 'READ']
+    required_checks = string_checks + ['#%d ' % line_ctr
+                                       for line_ctr in
+                                       range(stack_min_size)
+                                       ]
+    trace_indices = [trace.index(check) for check in required_checks]
+    new_line_index = trace.index("\n", trace_indices[-1])
+    return trace[:new_line_index + 1]
+
+
+def make_unique(trace):
+    """Removes overlapping line numbers and lines out of order."""
+    string_checks = ['use-after-poison', 'READ']
+    hard_checks = string_checks + ['#%d ' % line_ctr
+                                   for line_ctr in range(100)
+                                   ]
+    last_ind = -1
+    for str_check in hard_checks:
+        try:
+            location_ind = trace.index(str_check)
+            if last_ind > location_ind:
+                trace = trace[:trace[:location_ind].find("\n") + 1]
+            last_ind = location_ind
+            try:
+                next_location_ind = trace.index(str_check, location_ind + 1)
+                trace = trace[:next_location_ind]
+            except ValueError:
+                pass
+        except ValueError:
+            pass
+    return trace
+
+
+def parse_args(argv):
+    """Parses arguments passed in."""
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-d', action='store',
+                        default="", dest="out_dir_name", type=is_directory,
+                        help='Output Directory')
+    parser.add_argument('-e', action='store_true',
+                        default=False, dest='check_exact',
+                        help='Forces each trace to be cut to have '
+                             'minimum number of lines')
+    parser.add_argument('-m', action='store',
+                        default=4, dest='stack_min_size', type=int,
+                        help='minimum number of lines a trace should have')
+    parser.add_argument('trace_file', action='store',
+                        type=argparse.FileType('r'),
+                        help='File only containing lines that are related to '
+                             'Sanitizer traces')
+    return parser.parse_args(argv)
+
+
+def is_directory(path_name):
+    """Checks if a path is an actual directory."""
+    if not os.path.isdir(path_name):
+        dir_error = "%s is not a directory" % (path_name)
+        raise argparse.ArgumentTypeError(dir_error)
+    return path_name
+
+
+def main(argv=None):
+    """Parses arguments and cleans up traces using other functions."""
+    stack_min_size = 4
+    check_exact = False
+
+    if argv is None:
+        argv = sys.argv
+
+    parsed_argv = parse_args(argv[1:])
+    stack_min_size = parsed_argv.stack_min_size
+    check_exact = parsed_argv.check_exact
+    out_dir_name = parsed_argv.out_dir_name
+    trace_file = parsed_argv.trace_file
+
+    trace_split = trace_file.read().split(STACK_DIVIDER)
+    trace_file.close()
+    # if flag -e is enabled
+    if check_exact:
+        trace_prune_split = [prune_exact(trace, stack_min_size)
+                             for trace in trace_split if
+                             has_min_lines(trace, stack_min_size)
+                             ]
+        trace_unique_split = [make_unique(trace)
+                              for trace in trace_prune_split
+                              ]
+    else:
+        trace_unique_split = [make_unique(trace)
+                              for trace in trace_split if
+                              has_min_lines(trace, stack_min_size)
+                              ]
+    # has_min_lines is called again because removing lines can prune too much
+    trace_clean_split = [trace for trace
+                         in trace_unique_split if
+                         has_min_lines(trace,
+                                       stack_min_size)
+                         ]
+
+    outfile = os.path.join(out_dir_name, trace_file.name + '_filtered')
+    with open(outfile, "w") as output_file:
+        output_file.write(STACK_DIVIDER.join(trace_clean_split))
+
+    filter_percent = 100.0 - (float(len(trace_clean_split)) /
+                              len(trace_split) * 100)
+    filter_amount = len(trace_split) - len(trace_clean_split)
+    print("Filtered out %d (%f%%) of %d."
+          % (filter_amount, filter_percent, len(trace_split)))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tools/runtime_memusage/sanitizer_logcat_analysis.sh b/tools/runtime_memusage/sanitizer_logcat_analysis.sh
new file mode 100755
index 0000000..75cb9a9
--- /dev/null
+++ b/tools/runtime_memusage/sanitizer_logcat_analysis.sh
@@ -0,0 +1,180 @@
+#!/bin/sh
+#
+# Copyright (C) 2017 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.
+#
+# Note: Requires $ANDROID_BUILD_TOP/build/envsetup.sh to have been run.
+#
+# This script takes in a logcat containing Sanitizer traces and outputs several
+# files, prints information regarding the traces, and plots information as well.
+USE_TEMP=true
+DO_REDO=false
+# EXACT_ARG and MIN_ARG are passed to prune_sanitizer_output.py
+EXACT_ARG=""
+MIN_ARG=""
+usage() {
+  echo "Usage: $0 [options] [LOGCAT_FILE] [CATEGORIES...]"
+  echo "    -d  OUT_DIRECTORY"
+  echo "        Puts all output in specified directory."
+  echo "        If not given, output will be put in a local"
+  echo "        temp folder which will be deleted after"
+  echo "        execution."
+  echo
+  echo "    -e"
+  echo "        All traces will have exactly the same number"
+  echo "        of categories which is specified by either"
+  echo "        the -m argument or by prune_sanitizer_output.py"
+  echo
+  echo "    -f"
+  echo "        forces redo of all commands even if output"
+  echo "        files exist. Steps are skipped if their output"
+  echo "        exist already and this is not enabled."
+  echo
+  echo "    -m  [MINIMUM_CALLS_PER_TRACE]"
+  echo "        Filters out all traces that do not have"
+  echo "        at least MINIMUM_CALLS_PER_TRACE lines."
+  echo "        default: specified by prune_sanitizer_output.py"
+  echo
+  echo "    CATEGORIES are words that are expected to show in"
+  echo "       a large subset of symbolized traces. Splits"
+  echo "       output based on each word."
+  echo
+  echo "    LOGCAT_FILE is the piped output from adb logcat."
+  echo
+}
+
+
+while [[ $# -gt 1 ]]; do
+case $1 in
+  -d)
+  shift
+  USE_TEMP=false
+  OUT_DIR=$1
+  shift
+  break
+  ;;
+  -e)
+  shift
+  EXACT_ARG='-e'
+  ;;
+  -f)
+  shift
+  DO_REDO=true
+  ;;
+  -m)
+  shift
+  MIN_ARG='-m '"$1"''
+  shift
+  ;;
+  *)
+  usage
+  exit
+esac
+done
+
+if [ $# -lt 1 ]; then
+  usage
+  exit
+fi
+
+LOGCAT_FILE=$1
+NUM_CAT=$(($# - 1))
+
+# Use a temp directory that will be deleted
+if [ $USE_TEMP = true ]; then
+  OUT_DIR=$(mktemp -d --tmpdir=$PWD)
+  DO_REDO=true
+fi
+
+if [ ! -d "$OUT_DIR" ]; then
+  mkdir $OUT_DIR
+  DO_REDO=true
+fi
+
+# Note: Steps are skipped if their output exists until -f flag is enabled
+# Step 1 - Only output lines related to Sanitizer
+# Folder that holds all file output
+echo "Output folder: $OUT_DIR"
+ASAN_OUT=$OUT_DIR/asan_output
+if [ ! -f $ASAN_OUT ] || [ $DO_REDO = true ]; then
+  DO_REDO=true
+  echo "Extracting ASAN output"
+  grep "app_process64" $LOGCAT_FILE > $ASAN_OUT
+else
+  echo "Skipped: Extracting ASAN output"
+fi
+
+# Step 2 - Only output lines containing Dex File Start Addresses
+DEX_START=$OUT_DIR/dex_start
+if [ ! -f $DEX_START ] || [ $DO_REDO = true ]; then
+  DO_REDO=true
+  echo "Extracting Start of Dex File(s)"
+  grep "RegisterDexFile" $LOGCAT_FILE > $DEX_START
+else
+  echo "Skipped: Extracting Start of Dex File(s)"
+fi
+
+# Step 3 - Clean Sanitizer output from Step 2 since logcat cannot
+# handle large amounts of output.
+ASAN_OUT_FILTERED=$OUT_DIR/asan_output_filtered
+if [ ! -f $ASAN_OUT_FILTERED ] || [ $DO_REDO = true ]; then
+  DO_REDO=true
+  echo "Filtering/Cleaning ASAN output"
+  python $ANDROID_BUILD_TOP/art/tools/runtime_memusage/prune_sanitizer_output.py \
+  $EXACT_ARG $MIN_ARG -d $OUT_DIR $ASAN_OUT
+else
+  echo "Skipped: Filtering/Cleaning ASAN output"
+fi
+
+# Step 4 - Retrieve symbolized stack traces from Step 3 output
+SYM_FILTERED=$OUT_DIR/sym_filtered
+if [ ! -f $SYM_FILTERED ] || [ $DO_REDO = true ]; then
+  DO_REDO=true
+  echo "Retrieving symbolized traces"
+  $ANDROID_BUILD_TOP/development/scripts/stack $ASAN_OUT_FILTERED > $SYM_FILTERED
+else
+  echo "Skipped: Retrieving symbolized traces"
+fi
+
+# Step 5 - Using Steps 2, 3, 4 outputs in order to output graph data
+# and trace data
+# Only the category names are needed for the commands giving final output
+shift
+TIME_OUTPUT=($OUT_DIR/time_output_*.dat)
+if [ ! -e ${TIME_OUTPUT[0]} ] || [ $DO_REDO = true ]; then
+  DO_REDO=true
+  echo "Creating Categorized Time Table"
+  python $ANDROID_BUILD_TOP/art/tools/runtime_memusage/symbol_trace_info.py \
+    -d $OUT_DIR $ASAN_OUT_FILTERED $SYM_FILTERED $DEX_START $@
+else
+  echo "Skipped: Creating Categorized Time Table"
+fi
+
+# Step 6 - Use graph data from Step 5 to plot graph
+# Contains the category names used for legend of gnuplot
+PLOT_CATS=`echo \"Uncategorized $@\"`
+echo "Plotting Categorized Time Table"
+# Plots the information from logcat
+gnuplot --persist -e \
+  'filename(n) = sprintf("'"$OUT_DIR"'/time_output_%d.dat", n);
+   catnames = '"$PLOT_CATS"';
+   set title "Dex File Offset vs. Time accessed since App Start";
+   set xlabel "Time (milliseconds)";
+   set ylabel "Dex File Offset (bytes)";
+   plot for [i=0:'"$NUM_CAT"'] filename(i) using 1:2 title word(catnames, i + 1);'
+
+if [ $USE_TEMP = true ]; then
+  echo "Removing temp directory and files"
+  rm -rf $OUT_DIR
+fi
diff --git a/tools/runtime_memusage/symbol_trace_info.py b/tools/runtime_memusage/symbol_trace_info.py
new file mode 100755
index 0000000..57ed6ce
--- /dev/null
+++ b/tools/runtime_memusage/symbol_trace_info.py
@@ -0,0 +1,214 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2017 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.
+
+"""Outputs quantitative information about Address Sanitizer traces."""
+
+from __future__ import absolute_import
+from __future__ import division
+from __future__ import print_function
+
+from collections import Counter
+from datetime import datetime
+import argparse
+import bisect
+import os
+import sys
+
+
+def find_match(list_substrings, big_string):
+    """Returns the category a trace belongs to by searching substrings."""
+    for ind, substr in enumerate(list_substrings):
+        if big_string.find(substr) != -1:
+            return ind
+    return list_substrings.index("Uncategorized")
+
+
+def absolute_to_relative(plot_list, dex_start_list, cat_list):
+    """Address changed to Dex File offset and shifting time to 0 min in ms."""
+    time_format_str = "%H:%M:%S.%f"
+    first_access_time = datetime.strptime(plot_list[0][0],
+                                          time_format_str)
+    for ind, elem in enumerate(plot_list):
+        elem_date_time = datetime.strptime(elem[0], time_format_str)
+        # Shift time values so that first access is at time 0 milliseconds
+        elem[0] = int((elem_date_time - first_access_time).total_seconds() *
+                      1000)
+        address_access = int(elem[1], 16)
+        # For each poisoned address, find highest Dex File starting address less
+        # than address_access
+        dex_file_start = dex_start_list[bisect.bisect(dex_start_list,
+                                                      address_access) - 1
+                                        ]
+        elem.insert(1, address_access - dex_file_start)
+        # Category that a data point belongs to
+        elem.insert(2, cat_list[ind])
+
+
+def print_category_info(cat_split, outname, out_dir_name, title):
+    """Prints information of category and puts related traces in a files."""
+    trace_counts_dict = Counter(cat_split)
+    trace_counts_list_ordered = trace_counts_dict.most_common()
+    print(53 * "-")
+    print(title)
+    print("\tNumber of distinct traces: " +
+          str(len(trace_counts_list_ordered)))
+    print("\tSum of trace counts: " +
+          str(sum([trace[1] for trace in trace_counts_list_ordered])))
+    print("\n\tCount: How many traces appeared with count\n\t")
+    print(Counter([trace[1] for trace in trace_counts_list_ordered]))
+    with open(os.path.join(out_dir_name, outname), "w") as output_file:
+        for trace in trace_counts_list_ordered:
+            output_file.write("\n\nNumber of times appeared: " +
+                              str(trace[1]) +
+                              "\n")
+            output_file.write(trace[0].strip())
+
+
+def print_categories(categories, symbol_file_split, out_dir_name):
+    """Prints details of all categories."""
+    # Info of traces containing a call to current category
+    for cat_num, cat_name in enumerate(categories[1:]):
+        print("\nCategory #%d" % (cat_num + 1))
+        cat_split = [trace for trace in symbol_file_split
+                     if cat_name in trace]
+        cat_file_name = cat_name.lower() + "cat_output"
+        print_category_info(cat_split, cat_file_name, out_dir_name,
+                            "Traces containing: " + cat_name)
+        noncat_split = [trace for trace in symbol_file_split
+                        if cat_name not in trace]
+        print_category_info(noncat_split, "non" + cat_file_name,
+                            out_dir_name,
+                            "Traces not containing: " +
+                            cat_name)
+
+    # All traces (including uncategorized) together
+    print_category_info(symbol_file_split, "allcat_output",
+                        out_dir_name,
+                        "All traces together:")
+    # Traces containing none of keywords
+    # Only used if categories are passed in
+    if len(categories) > 1:
+        noncat_split = [trace for trace in symbol_file_split if
+                        all(cat_name not in trace
+                            for cat_name in categories)]
+        print_category_info(noncat_split, "noncat_output",
+                            out_dir_name,
+                            "Uncategorized calls")
+
+
+def is_directory(path_name):
+    """Checks if a path is an actual directory."""
+    if not os.path.isdir(path_name):
+        dir_error = "%s is not a directory" % (path_name)
+        raise argparse.ArgumentTypeError(dir_error)
+    return path_name
+
+
+def parse_args(argv):
+    """Parses arguments passed in."""
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-d', action='store',
+                        default="", dest="out_dir_name", type=is_directory,
+                        help='Output Directory')
+    parser.add_argument('sanitizer_trace', action='store',
+                        type=argparse.FileType('r'),
+                        help='File containing sanitizer traces filtered by '
+                             'prune_sanitizer_output.py')
+    parser.add_argument('symbol_trace', action='store',
+                        type=argparse.FileType('r'),
+                        help='File containing symbolized traces that match '
+                             'sanitizer_trace')
+    parser.add_argument('dex_starts', action='store',
+                        type=argparse.FileType('r'),
+                        help='File containing starting addresses of Dex Files')
+    parser.add_argument('categories', action='store', nargs='*',
+                        help='Keywords expected to show in large amounts of'
+                             ' symbolized traces')
+
+    return parser.parse_args(argv)
+
+
+def read_data(parsed_argv):
+    """Reads data from filepath arguments and parses them into lists."""
+    # Using a dictionary to establish relation between lists added
+    data_lists = {}
+    categories = parsed_argv.categories
+    # Makes sure each trace maps to some category
+    categories.insert(0, "Uncategorized")
+
+    logcat_file_data = parsed_argv.sanitizer_trace.readlines()
+    parsed_argv.sanitizer_trace.close()
+
+    symbol_file_split = parsed_argv.symbol_trace.read().split("Stack Trace")[
+        1:]
+    parsed_argv.symbol_trace.close()
+
+    dex_start_file_data = parsed_argv.dex_starts.readlines()
+    parsed_argv.dex_starts.close()
+
+    # Each element is a tuple of time and address accessed
+    data_lists["plot_list"] = [[elem[1] for elem in enumerate(line.split())
+                                if elem[0] in (1, 11)
+                                ]
+                               for line in logcat_file_data
+                               if "use-after-poison" in line
+                               ]
+    # Contains a mapping between traces and the category they belong to
+    # based on arguments
+    data_lists["cat_list"] = [categories[find_match(categories, trace)]
+                              for trace in symbol_file_split]
+
+    # Contains a list of starting address of all dex files to calculate dex
+    # offsets
+    data_lists["dex_start_list"] = [int(line.split("@")[1], 16)
+                                    for line in dex_start_file_data
+                                    if "RegisterDexFile" in line
+                                    ]
+    return data_lists, categories, symbol_file_split
+
+
+def main(argv=None):
+    """Takes in trace information and outputs details about them."""
+    if argv is None:
+        argv = sys.argv
+    parsed_argv = parse_args(argv[1:])
+
+    data_lists, categories, symbol_file_split = read_data(parsed_argv)
+    # Formats plot_list such that each element is a data point
+    absolute_to_relative(data_lists["plot_list"], data_lists["dex_start_list"],
+                         data_lists["cat_list"])
+    for file_ext, cat_name in enumerate(categories):
+        out_file_name = os.path.join(parsed_argv.out_dir_name, "time_output_" +
+                                     str(file_ext) +
+                                     ".dat")
+        with open(out_file_name, "w") as output_file:
+            output_file.write("# Category: " + cat_name + "\n")
+            output_file.write("# Time, Dex File Offset, Address \n")
+            for time, dex_offset, category, address in data_lists["plot_list"]:
+                if category == cat_name:
+                    output_file.write(
+                        str(time) +
+                        " " +
+                        str(dex_offset) +
+                        " #" +
+                        str(address) +
+                        "\n")
+
+    print_categories(categories, symbol_file_split, parsed_argv.out_dir_name)
+
+
+if __name__ == '__main__':
+    main()