Merge "SIMD pcmpgtb,w,d,q for x86/x86_64"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index cf1832b..0ed230c 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -1,2 +1,3 @@
 [Hook Scripts]
 check_generated_files_up_to_date = tools/cpp-define-generator/presubmit-check-files-up-to-date
+check_cpplint_on_changed_files = tools/cpplint_presubmit.py
diff --git a/compiler/utils/assembler_test.h b/compiler/utils/assembler_test.h
index d265a44..f655994 100644
--- a/compiler/utils/assembler_test.h
+++ b/compiler/utils/assembler_test.h
@@ -309,7 +309,7 @@
   template <typename RegType, typename ImmType>
   std::string RepeatTemplatedRegisterImmBits(void (Ass::*f)(RegType, ImmType),
                                              int imm_bits,
-                                             const std::vector<Reg*> registers,
+                                             const std::vector<RegType*> registers,
                                              std::string (AssemblerTest::*GetName)(const RegType&),
                                              const std::string& fmt,
                                              int bias) {
@@ -573,6 +573,19 @@
   }
 
   template <typename ImmType>
+  std::string RepeatVIb(void (Ass::*f)(VecReg, ImmType),
+                        int imm_bits,
+                        std::string fmt,
+                        int bias = 0) {
+    return RepeatTemplatedRegisterImmBits<VecReg, ImmType>(f,
+                                                           imm_bits,
+                                                           GetVectorRegisters(),
+                                                           &AssemblerTest::GetVecRegName,
+                                                           fmt,
+                                                           bias);
+  }
+
+  template <typename ImmType>
   std::string RepeatVRIb(void (Ass::*f)(VecReg, Reg, ImmType),
                          int imm_bits,
                          const std::string& fmt,
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 7d6a7f8..0cff44d 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -252,6 +252,22 @@
   Emit(encoding);
 }
 
+void Mips64Assembler::EmitMsaI10(int operation,
+                                 int df,
+                                 int i10,
+                                 VectorRegister wd,
+                                 int minor_opcode) {
+  CHECK_NE(wd, kNoVectorRegister);
+  CHECK(IsUint<10>(i10)) << i10;
+  uint32_t encoding = static_cast<uint32_t>(kMsaMajorOpcode) << kOpcodeShift |
+                      operation << kMsaOperationShift |
+                      df << kDfShift |
+                      i10 << kI10Shift |
+                      static_cast<uint32_t>(wd) << kWdShift |
+                      minor_opcode;
+  Emit(encoding);
+}
+
 void Mips64Assembler::EmitMsa2R(int operation,
                                 int df,
                                 VectorRegister ws,
@@ -1581,6 +1597,30 @@
   EmitMsa2R(0xc0, 0x3, static_cast<VectorRegister>(rs), wd, 0x1e);
 }
 
+void Mips64Assembler::LdiB(VectorRegister wd, int imm8) {
+  CHECK(HasMsa());
+  CHECK(IsInt<8>(imm8)) << imm8;
+  EmitMsaI10(0x6, 0x0, imm8 & kMsaS10Mask, wd, 0x7);
+}
+
+void Mips64Assembler::LdiH(VectorRegister wd, int imm10) {
+  CHECK(HasMsa());
+  CHECK(IsInt<10>(imm10)) << imm10;
+  EmitMsaI10(0x6, 0x1, imm10 & kMsaS10Mask, wd, 0x7);
+}
+
+void Mips64Assembler::LdiW(VectorRegister wd, int imm10) {
+  CHECK(HasMsa());
+  CHECK(IsInt<10>(imm10)) << imm10;
+  EmitMsaI10(0x6, 0x2, imm10 & kMsaS10Mask, wd, 0x7);
+}
+
+void Mips64Assembler::LdiD(VectorRegister wd, int imm10) {
+  CHECK(HasMsa());
+  CHECK(IsInt<10>(imm10)) << imm10;
+  EmitMsaI10(0x6, 0x3, imm10 & kMsaS10Mask, wd, 0x7);
+}
+
 void Mips64Assembler::LdB(VectorRegister wd, GpuRegister rs, int offset) {
   CHECK(HasMsa());
   CHECK(IsInt<10>(offset)) << offset;
diff --git a/compiler/utils/mips64/assembler_mips64.h b/compiler/utils/mips64/assembler_mips64.h
index a8035b6..666c693 100644
--- a/compiler/utils/mips64/assembler_mips64.h
+++ b/compiler/utils/mips64/assembler_mips64.h
@@ -734,6 +734,10 @@
   void FillW(VectorRegister wd, GpuRegister rs);
   void FillD(VectorRegister wd, GpuRegister rs);
 
+  void LdiB(VectorRegister wd, int imm8);
+  void LdiH(VectorRegister wd, int imm10);
+  void LdiW(VectorRegister wd, int imm10);
+  void LdiD(VectorRegister wd, int imm10);
   void LdB(VectorRegister wd, GpuRegister rs, int offset);
   void LdH(VectorRegister wd, GpuRegister rs, int offset);
   void LdW(VectorRegister wd, GpuRegister rs, int offset);
@@ -1457,6 +1461,7 @@
   void EmitMsaBIT(int operation, int df_m, VectorRegister ws, VectorRegister wd, int minor_opcode);
   void EmitMsaELM(int operation, int df_n, VectorRegister ws, VectorRegister wd, int minor_opcode);
   void EmitMsaMI10(int s10, GpuRegister rs, VectorRegister wd, int minor_opcode, int df);
+  void EmitMsaI10(int operation, int df, int i10, VectorRegister wd, int minor_opcode);
   void EmitMsa2R(int operation, int df, VectorRegister ws, VectorRegister wd, int minor_opcode);
   void EmitMsa2RF(int operation, int df, VectorRegister ws, VectorRegister wd, int minor_opcode);
 
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index cadbe27..f2e3b16 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -2836,6 +2836,22 @@
   DriverStr(RepeatVR(&mips64::Mips64Assembler::FillD, "fill.d ${reg1}, ${reg2}"), "fill.d");
 }
 
+TEST_F(AssemblerMIPS64Test, LdiB) {
+  DriverStr(RepeatVIb(&mips64::Mips64Assembler::LdiB, -8, "ldi.b ${reg}, {imm}"), "ldi.b");
+}
+
+TEST_F(AssemblerMIPS64Test, LdiH) {
+  DriverStr(RepeatVIb(&mips64::Mips64Assembler::LdiH, -10, "ldi.h ${reg}, {imm}"), "ldi.h");
+}
+
+TEST_F(AssemblerMIPS64Test, LdiW) {
+  DriverStr(RepeatVIb(&mips64::Mips64Assembler::LdiW, -10, "ldi.w ${reg}, {imm}"), "ldi.w");
+}
+
+TEST_F(AssemblerMIPS64Test, LdiD) {
+  DriverStr(RepeatVIb(&mips64::Mips64Assembler::LdiD, -10, "ldi.d ${reg}, {imm}"), "ldi.d");
+}
+
 TEST_F(AssemblerMIPS64Test, LdB) {
   DriverStr(RepeatVRIb(&mips64::Mips64Assembler::LdB, -10, "ld.b ${reg1}, {imm}(${reg2})"), "ld.b");
 }
diff --git a/compiler/utils/mips64/constants_mips64.h b/compiler/utils/mips64/constants_mips64.h
index 5ae9c73..bc8e40b 100644
--- a/compiler/utils/mips64/constants_mips64.h
+++ b/compiler/utils/mips64/constants_mips64.h
@@ -66,6 +66,7 @@
   kWdShift = 6,
   kWdBits = 5,
   kS10Shift = 16,
+  kI10Shift = 11,
   kS10MinorShift = 2,
 
   kBranchOffsetMask = 0x0000ffff,
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index cf523ec..e26d051 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -56,3 +56,16 @@
     defaults: ["art_gtest_defaults"],
     srcs: ["dexlayout_test.cc"],
 }
+
+art_cc_binary {
+    name: "dexdiag",
+    host_supported: false,
+    srcs: ["dexdiag.cc"],
+    cflags: ["-Wall"],
+    shared_libs: [
+        "libart",
+        "libart-dexlayout",
+        "libpagemap",
+    ],
+}
+
diff --git a/dexlayout/dexdiag.cc b/dexlayout/dexdiag.cc
new file mode 100644
index 0000000..211bfdf
--- /dev/null
+++ b/dexlayout/dexdiag.cc
@@ -0,0 +1,405 @@
+/*
+ * 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.
+ */
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <iostream>
+#include <memory>
+
+#include "android-base/stringprintf.h"
+
+#include "dex_file.h"
+#include "dex_ir.h"
+#include "dex_ir_builder.h"
+#include "pagemap/pagemap.h"
+#include "runtime.h"
+#include "vdex_file.h"
+
+namespace art {
+
+using android::base::StringPrintf;
+
+static constexpr size_t kLineLength = 32;
+
+static bool g_show_key = false;
+static bool g_verbose = false;
+static bool g_show_statistics = false;
+
+struct DexSectionInfo {
+ public:
+  std::string name;
+  char letter;
+};
+
+static const std::map<uint16_t, DexSectionInfo> kDexSectionInfoMap = {
+  { DexFile::kDexTypeHeaderItem, { "Header", 'H' } },
+  { DexFile::kDexTypeStringIdItem, { "StringId", 'S' } },
+  { DexFile::kDexTypeTypeIdItem, { "TypeId", 'T' } },
+  { DexFile::kDexTypeProtoIdItem, { "ProtoId", 'P' } },
+  { DexFile::kDexTypeFieldIdItem, { "FieldId", 'F' } },
+  { DexFile::kDexTypeMethodIdItem, { "MethodId", 'M' } },
+  { DexFile::kDexTypeClassDefItem, { "ClassDef", 'C' } },
+  { DexFile::kDexTypeCallSiteIdItem, { "CallSiteId", 'z' } },
+  { DexFile::kDexTypeMethodHandleItem, { "MethodHandle", 'Z' } },
+  { DexFile::kDexTypeMapList, { "TypeMap", 'L' } },
+  { DexFile::kDexTypeTypeList, { "TypeList", 't' } },
+  { DexFile::kDexTypeAnnotationSetRefList, { "AnnotationSetReferenceItem", '1' } },
+  { DexFile::kDexTypeAnnotationSetItem, { "AnnotationSetItem", '2' } },
+  { DexFile::kDexTypeClassDataItem, { "ClassData", 'c' } },
+  { DexFile::kDexTypeCodeItem, { "CodeItem", 'X' } },
+  { DexFile::kDexTypeStringDataItem, { "StringData", 's' } },
+  { DexFile::kDexTypeDebugInfoItem, { "DebugInfo", 'D' } },
+  { DexFile::kDexTypeAnnotationItem, { "AnnotationItem", '3' } },
+  { DexFile::kDexTypeEncodedArrayItem, { "EncodedArrayItem", 'E' } },
+  { DexFile::kDexTypeAnnotationsDirectoryItem, { "AnnotationsDirectoryItem", '4' } }
+};
+
+class PageCount {
+ public:
+  PageCount() {
+    for (auto it = kDexSectionInfoMap.begin(); it != kDexSectionInfoMap.end(); ++it) {
+      map_[it->first] = 0;
+    }
+  }
+  void Increment(uint16_t type) {
+    map_[type]++;
+  }
+  size_t Get(uint16_t type) const {
+    return map_.at(type);
+  }
+ private:
+  std::map<uint16_t, size_t> map_;
+  DISALLOW_COPY_AND_ASSIGN(PageCount);
+};
+
+static void PrintLetterKey() {
+  std::cout << "letter section_type" << std::endl;
+  for (const auto& p : kDexSectionInfoMap) {
+    const DexSectionInfo& section_info = p.second;
+    std::cout << section_info.letter << "      " << section_info.name.c_str() << std::endl;
+  }
+}
+
+static char PageTypeChar(uint16_t type) {
+  if (kDexSectionInfoMap.find(type) == kDexSectionInfoMap.end()) {
+    return '-';
+  }
+  return kDexSectionInfoMap.find(type)->second.letter;
+}
+
+static uint16_t FindSectionTypeForPage(size_t page,
+                                       const std::vector<dex_ir::DexFileSection>& sections) {
+  for (const auto& section : sections) {
+    size_t first_page_of_section = section.offset / kPageSize;
+    // Only consider non-empty sections.
+    if (section.size == 0) {
+      continue;
+    }
+    // Attribute the page to the highest-offset section that starts before the page.
+    if (first_page_of_section <= page) {
+      return section.type;
+    }
+  }
+  // If there's no non-zero sized section with an offset below offset we're looking for, it
+  // must be the header.
+  return DexFile::kDexTypeHeaderItem;
+}
+
+static void ProcessPageMap(uint64_t* pagemap,
+                           size_t start,
+                           size_t end,
+                           const std::vector<dex_ir::DexFileSection>& sections,
+                           PageCount* page_counts) {
+  for (size_t page = start; page < end; ++page) {
+    char type_char = '.';
+    if (PM_PAGEMAP_PRESENT(pagemap[page])) {
+      uint16_t type = FindSectionTypeForPage(page, sections);
+      page_counts->Increment(type);
+      type_char = PageTypeChar(type);
+    }
+    if (g_verbose) {
+      std::cout << type_char;
+      if ((page - start) % kLineLength == kLineLength - 1) {
+        std::cout << std::endl;
+      }
+    }
+  }
+  if (g_verbose) {
+    if ((end - start) % kLineLength != 0) {
+      std::cout << std::endl;
+    }
+  }
+}
+
+static void DisplayDexStatistics(size_t start,
+                                 size_t end,
+                                 const PageCount& resident_pages,
+                                 const std::vector<dex_ir::DexFileSection>& sections) {
+  // Compute the total possible sizes for sections.
+  PageCount mapped_pages;
+  DCHECK_GE(end, start);
+  size_t total_mapped_pages = end - start;
+  if (total_mapped_pages == 0) {
+    return;
+  }
+  for (size_t page = start; page < end; ++page) {
+    mapped_pages.Increment(FindSectionTypeForPage(page, sections));
+  }
+  size_t total_resident_pages = 0;
+  // Compute the width of the section header column in the table (for fixed formatting).
+  int section_header_width = 0;
+  for (const auto& section_info : kDexSectionInfoMap) {
+    section_header_width = std::max(section_header_width,
+                                    static_cast<int>(section_info.second.name.length()));
+  }
+  // The width needed to print a file page offset (32-bit).
+  static constexpr int kPageCountWidth =
+      static_cast<int>(std::numeric_limits<uint32_t>::digits10);
+  // Display the sections.
+  static constexpr char kSectionHeader[] = "Section name";
+  std::cout << StringPrintf("%-*s %*s %*s %% of   %% of",
+                            section_header_width,
+                            kSectionHeader,
+                            kPageCountWidth,
+                            "resident",
+                            kPageCountWidth,
+                            "total"
+                            )
+            << std::endl;
+  std::cout << StringPrintf("%-*s %*s %*s sect.  total",
+                            section_header_width,
+                            "",
+                            kPageCountWidth,
+                            "pages",
+                            kPageCountWidth,
+                            "pages")
+            << std::endl;
+  for (size_t i = sections.size(); i > 0; --i) {
+    const dex_ir::DexFileSection& section = sections[i - 1];
+    const uint16_t type = section.type;
+    const DexSectionInfo& section_info = kDexSectionInfoMap.find(type)->second;
+    size_t pages_resident = resident_pages.Get(type);
+    double percent_resident = 0;
+    if (mapped_pages.Get(type) > 0) {
+      percent_resident = 100.0 * pages_resident / mapped_pages.Get(type);
+    }
+    // 6.2 is sufficient to print 0-100% with two decimal places of accuracy.
+    std::cout << StringPrintf("%-*s %*zd %*zd %6.2f %6.2f",
+                              section_header_width,
+                              section_info.name.c_str(),
+                              kPageCountWidth,
+                              pages_resident,
+                              kPageCountWidth,
+                              mapped_pages.Get(type),
+                              percent_resident,
+                              100.0 * pages_resident / total_mapped_pages)
+              << std::endl;
+    total_resident_pages += pages_resident;
+  }
+  std::cout << StringPrintf("%-*s %*zd %*zd        %6.2f",
+                            section_header_width,
+                            "GRAND TOTAL",
+                            kPageCountWidth,
+                            total_resident_pages,
+                            kPageCountWidth,
+                            total_mapped_pages,
+                            100.0 * total_resident_pages / total_mapped_pages)
+            << std::endl
+            << std::endl;
+}
+
+static void ProcessOneDexMapping(uint64_t* pagemap,
+                                 uint64_t map_start,
+                                 const DexFile* dex_file,
+                                 uint64_t vdex_start) {
+  uint64_t dex_file_start = reinterpret_cast<uint64_t>(dex_file->Begin());
+  size_t dex_file_size = dex_file->Size();
+  if (dex_file_start < vdex_start) {
+    std::cerr << "Dex file start offset for "
+              << dex_file->GetLocation().c_str()
+              << " is incorrect: map start "
+              << StringPrintf("%zx > dex start %zx\n", map_start, dex_file_start)
+              << std::endl;
+    return;
+  }
+  uint64_t start = (dex_file_start - vdex_start) / kPageSize;
+  uint64_t end = RoundUp(start + dex_file_size, kPageSize) / kPageSize;
+  std::cout << "DEX "
+            << dex_file->GetLocation().c_str()
+            << StringPrintf(": %zx-%zx",
+                            map_start + start * kPageSize,
+                            map_start + end * kPageSize)
+            << std::endl;
+  // Build a list of the dex file section types, sorted from highest offset to lowest.
+  std::vector<dex_ir::DexFileSection> sections;
+  {
+    std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file));
+    sections = dex_ir::GetSortedDexFileSections(header.get(),
+                                                dex_ir::SortDirection::kSortDescending);
+  }
+  PageCount section_resident_pages;
+  ProcessPageMap(pagemap, start, end, sections, &section_resident_pages);
+  if (g_show_statistics) {
+    DisplayDexStatistics(start, end, section_resident_pages, sections);
+  }
+}
+
+static bool DisplayMappingIfFromVdexFile(pm_map_t* map) {
+  // Confirm that the map is from a vdex file.
+  static const char* suffixes[] = { ".vdex" };
+  std::string vdex_name;
+  bool found = false;
+  for (size_t j = 0; j < sizeof(suffixes) / sizeof(suffixes[0]); ++j) {
+    if (strstr(pm_map_name(map), suffixes[j]) != nullptr) {
+      vdex_name = pm_map_name(map);
+      found = true;
+      break;
+    }
+  }
+  if (!found) {
+    return true;
+  }
+  // Extract all the dex files from the vdex file.
+  std::string error_msg;
+  std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_name,
+                                                false /*writeable*/,
+                                                false /*low_4gb*/,
+                                                &error_msg /*out*/));
+  if (vdex == nullptr) {
+    std::cerr << "Could not open vdex file "
+              << vdex_name.c_str()
+              << ": error "
+              << error_msg.c_str()
+              << std::endl;
+    return false;
+  }
+
+  std::vector<std::unique_ptr<const DexFile>> dex_files;
+  if (!vdex->OpenAllDexFiles(&dex_files, &error_msg)) {
+    std::cerr << "Dex files could not be opened for "
+              << vdex_name.c_str()
+              << ": error "
+              << error_msg.c_str()
+              << std::endl;
+  }
+  // Open the page mapping (one uint64_t per page) for the entire vdex mapping.
+  uint64_t* pagemap;
+  size_t len;
+  if (pm_map_pagemap(map, &pagemap, &len) != 0) {
+    std::cerr << "Error creating pagemap." << std::endl;
+    return false;
+  }
+  // Process the dex files.
+  std::cout << "MAPPING "
+            << pm_map_name(map)
+            << StringPrintf(": %zx-%zx", pm_map_start(map), pm_map_end(map))
+            << std::endl;
+  for (const auto& dex_file : dex_files) {
+    ProcessOneDexMapping(pagemap,
+                         pm_map_start(map),
+                         dex_file.get(),
+                         reinterpret_cast<uint64_t>(vdex->Begin()));
+  }
+  free(pagemap);
+  return true;
+}
+
+
+static void Usage(const char* cmd) {
+  std::cerr << "Usage: " << cmd << " [-k] [-s] [-v] pid" << std::endl
+            << "    -k Shows a key to verbose display characters." << std::endl
+            << "    -s Shows section statistics for individual dex files." << std::endl
+            << "    -v Verbosely displays resident pages for dex files." << std::endl;
+}
+
+static int DexDiagMain(int argc, char* argv[]) {
+  if (argc < 2) {
+    Usage(argv[0]);
+    return EXIT_FAILURE;
+  }
+
+  // TODO: add option to track usage by class name, etc.
+  for (int i = 1; i < argc - 1; ++i) {
+    if (strcmp(argv[i], "-k") == 0) {
+      g_show_key = true;
+    } else if (strcmp(argv[i], "-s") == 0) {
+      g_show_statistics = true;
+    } else if (strcmp(argv[i], "-v") == 0) {
+      g_verbose = true;
+    } else {
+      Usage(argv[0]);
+      return EXIT_FAILURE;
+    }
+  }
+
+  // Art specific set up.
+  InitLogging(argv, Runtime::Aborter);
+  MemMap::Init();
+
+  pid_t pid;
+  char* endptr;
+  pid = (pid_t)strtol(argv[argc - 1], &endptr, 10);
+  if (*endptr != '\0' || kill(pid, 0) != 0) {
+    std::cerr << StringPrintf("Invalid PID \"%s\".\n", argv[argc - 1]) << std::endl;
+    return EXIT_FAILURE;
+  }
+
+  // get libpagemap kernel information.
+  pm_kernel_t* ker;
+  if (pm_kernel_create(&ker) != 0) {
+    std::cerr << "Error creating kernel interface -- does this kernel have pagemap?" << std::endl;
+    return EXIT_FAILURE;
+  }
+
+  // get libpagemap process information.
+  pm_process_t* proc;
+  if (pm_process_create(ker, pid, &proc) != 0) {
+    std::cerr << "Error creating process interface -- does process "
+              << pid
+              << " really exist?"
+              << std::endl;
+    return EXIT_FAILURE;
+  }
+
+  // Get the set of mappings by the specified process.
+  pm_map_t** maps;
+  size_t num_maps;
+  if (pm_process_maps(proc, &maps, &num_maps) != 0) {
+    std::cerr << "Error listing maps." << std::endl;
+    return EXIT_FAILURE;
+  }
+
+  // Process the mappings that are due to DEX files.
+  for (size_t i = 0; i < num_maps; ++i) {
+    if (!DisplayMappingIfFromVdexFile(maps[i])) {
+      return EXIT_FAILURE;
+    }
+  }
+
+  if (g_show_key) {
+    PrintLetterKey();
+  }
+  return 0;
+}
+
+}  // namespace art
+
+int main(int argc, char* argv[]) {
+  return art::DexDiagMain(argc, argv);
+}
diff --git a/disassembler/disassembler_mips.cc b/disassembler/disassembler_mips.cc
index fc6c18b..eb57d33 100644
--- a/disassembler/disassembler_mips.cc
+++ b/disassembler/disassembler_mips.cc
@@ -448,6 +448,7 @@
   { kMsaMask | (0x3ff << 16), kMsa | (0xbe << 16) | 0x19, "move.v", "km" },
   { kMsaMask | (0xf << 22), kMsa | (0x1 << 22) | 0x19, "splati", "kX" },
   { kMsaMask | (0xff << 18), kMsa | (0xc0 << 18) | 0x1e, "fill", "vkD" },
+  { kMsaMask | (0x7 << 23), kMsa | (0x6 << 23) | 0x7, "ldi", "kx" },
   { kMsaSpecialMask | (0xf << 2), kMsa | (0x8 << 2), "ld", "kw" },
   { kMsaSpecialMask | (0xf << 2), kMsa | (0x9 << 2), "st", "kw" },
 };
@@ -697,6 +698,20 @@
               }
               break;
             }
+          case 'x':  // MSA i10.
+            {
+              int32_t df = (instruction >> 21) & 0x3;
+              int32_t i10 = (instruction >> 11) & 0x3ff;
+              i10 -= (i10 & 0x200) << 1;  // Sign-extend i10.
+              switch (df) {
+                case 0: opcode += ".b"; break;
+                case 1: opcode += ".h"; break;
+                case 2: opcode += ".w"; break;
+                case 3: opcode += ".d"; break;
+              }
+              args << i10;
+              break;
+            }
         }
         if (*(args_fmt + 1)) {
           args << ", ";
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 78ba6e7..15724a1 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -68,6 +68,74 @@
 
 using android::base::StringPrintf;
 
+static const uint8_t kBase64Map[256] = {
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
+  52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
+  255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
+    7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,  // NOLINT
+   19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,  // NOLINT
+  255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
+   37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  // NOLINT
+   49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,  // NOLINT
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+  255, 255, 255, 255
+};
+
+uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
+  CHECK(dst_size != nullptr);
+  std::vector<uint8_t> tmp;
+  uint32_t t = 0, y = 0;
+  int g = 3;
+  for (size_t i = 0; src[i] != '\0'; ++i) {
+    uint8_t c = kBase64Map[src[i] & 0xFF];
+    if (c == 255) continue;
+    // the final = symbols are read and used to trim the remaining bytes
+    if (c == 254) {
+      c = 0;
+      // prevent g < 0 which would potentially allow an overflow later
+      if (--g < 0) {
+        *dst_size = 0;
+        return nullptr;
+      }
+    } else if (g != 3) {
+      // we only allow = to be at the end
+      *dst_size = 0;
+      return nullptr;
+    }
+    t = (t << 6) | c;
+    if (++y == 4) {
+      tmp.push_back((t >> 16) & 255);
+      if (g > 1) {
+        tmp.push_back((t >> 8) & 255);
+      }
+      if (g > 2) {
+        tmp.push_back(t & 255);
+      }
+      y = t = 0;
+    }
+  }
+  if (y != 0) {
+    *dst_size = 0;
+    return nullptr;
+  }
+  std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
+  *dst_size = tmp.size();
+  std::copy(tmp.begin(), tmp.end(), dst.get());
+  return dst.release();
+}
+
 ScratchFile::ScratchFile() {
   // ANDROID_DATA needs to be set
   CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")) <<
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index d7abe2a..bfa273d 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -44,6 +44,8 @@
 class Runtime;
 typedef std::vector<std::pair<std::string, const void*>> RuntimeOptions;
 
+uint8_t* DecodeBase64(const char* src, size_t* dst_size);
+
 class ScratchFile {
  public:
   ScratchFile();
diff --git a/runtime/jit/profile_saver.cc b/runtime/jit/profile_saver.cc
index e2bd1cb..9d6cd95 100644
--- a/runtime/jit/profile_saver.cc
+++ b/runtime/jit/profile_saver.cc
@@ -451,9 +451,6 @@
       return;
     }
     instance_->shutting_down_ = true;
-    if (dump_info) {
-      instance_->DumpInfo(LOG_STREAM(INFO));
-    }
   }
 
   {
@@ -470,6 +467,9 @@
 
   {
     MutexLock profiler_mutex(Thread::Current(), *Locks::profiler_lock_);
+    if (dump_info) {
+      instance_->DumpInfo(LOG_STREAM(INFO));
+    }
     instance_ = nullptr;
     profiler_pthread_ = 0U;
   }
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 44f8281..48efbe5 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -806,11 +806,11 @@
   // before fork aren't attributed to an app.
   heap_->ResetGcPerformanceInfo();
 
-
-  if (!is_system_server &&
+  // We may want to collect profiling samples for system server, but we never want to JIT there.
+  if ((!is_system_server || !jit_options_->UseJitCompilation()) &&
       !safe_mode_ &&
       (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) &&
-      jit_.get() == nullptr) {
+      jit_ == nullptr) {
     // Note that when running ART standalone (not zygote, nor zygote fork),
     // the jit may have already been created.
     CreateJit();
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 6a20eaf..8d216ce 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -52,77 +52,6 @@
 using android::base::StringAppendF;
 using android::base::StringPrintf;
 
-static const uint8_t kBase64Map[256] = {
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255,  62, 255, 255, 255,  63,
-  52,  53,  54,  55,  56,  57,  58,  59,  60,  61, 255, 255,
-  255, 254, 255, 255, 255,   0,   1,   2,   3,   4,   5,   6,
-    7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,  // NOLINT
-   19,  20,  21,  22,  23,  24,  25, 255, 255, 255, 255, 255,  // NOLINT
-  255,  26,  27,  28,  29,  30,  31,  32,  33,  34,  35,  36,
-   37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  // NOLINT
-   49,  50,  51, 255, 255, 255, 255, 255, 255, 255, 255, 255,  // NOLINT
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
-  255, 255, 255, 255
-};
-
-uint8_t* DecodeBase64(const char* src, size_t* dst_size) {
-  std::vector<uint8_t> tmp;
-  uint32_t t = 0, y = 0;
-  int g = 3;
-  for (size_t i = 0; src[i] != '\0'; ++i) {
-    uint8_t c = kBase64Map[src[i] & 0xFF];
-    if (c == 255) continue;
-    // the final = symbols are read and used to trim the remaining bytes
-    if (c == 254) {
-      c = 0;
-      // prevent g < 0 which would potentially allow an overflow later
-      if (--g < 0) {
-        *dst_size = 0;
-        return nullptr;
-      }
-    } else if (g != 3) {
-      // we only allow = to be at the end
-      *dst_size = 0;
-      return nullptr;
-    }
-    t = (t << 6) | c;
-    if (++y == 4) {
-      tmp.push_back((t >> 16) & 255);
-      if (g > 1) {
-        tmp.push_back((t >> 8) & 255);
-      }
-      if (g > 2) {
-        tmp.push_back(t & 255);
-      }
-      y = t = 0;
-    }
-  }
-  if (y != 0) {
-    *dst_size = 0;
-    return nullptr;
-  }
-  std::unique_ptr<uint8_t[]> dst(new uint8_t[tmp.size()]);
-  if (dst_size != nullptr) {
-    *dst_size = tmp.size();
-  } else {
-    *dst_size = 0;
-  }
-  std::copy(tmp.begin(), tmp.end(), dst.get());
-  return dst.release();
-}
-
 pid_t GetTid() {
 #if defined(__APPLE__)
   uint64_t owner;
diff --git a/runtime/utils.h b/runtime/utils.h
index 24fd205..2011d9e 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -70,8 +70,6 @@
   return intp & 0xFFFFFFFFU;
 }
 
-uint8_t* DecodeBase64(const char* src, size_t* dst_size);
-
 std::string PrintableChar(uint16_t ch);
 
 // Returns an ASCII string corresponding to the given UTF-8 string.
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 2481c8b..9ff104b 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -120,4 +120,30 @@
   }
 }
 
+bool VdexFile::OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files,
+                               std::string* error_msg) {
+  size_t i = 0;
+  for (const uint8_t* dex_file_start = GetNextDexFileData(nullptr);
+       dex_file_start != nullptr;
+       dex_file_start = GetNextDexFileData(dex_file_start), ++i) {
+    size_t size = reinterpret_cast<const DexFile::Header*>(dex_file_start)->file_size_;
+    // TODO: Supply the location information for a vdex file.
+    static constexpr char kVdexLocation[] = "";
+    std::string location = DexFile::GetMultiDexLocation(i, kVdexLocation);
+    std::unique_ptr<const DexFile> dex(DexFile::Open(dex_file_start,
+                                                     size,
+                                                     location,
+                                                     GetLocationChecksum(i),
+                                                     nullptr /*oat_dex_file*/,
+                                                     false /*verify*/,
+                                                     false /*verify_checksum*/,
+                                                     error_msg));
+    if (dex == nullptr) {
+      return false;
+    }
+    dex_files->push_back(std::move(dex));
+  }
+  return true;
+}
+
 }  // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 898d07d..9840555 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -27,6 +27,8 @@
 
 namespace art {
 
+class DexFile;
+
 // VDEX files contain extracted DEX files. The VdexFile class maps the file to
 // memory and provides tools for accessing its individual sections.
 //
@@ -122,6 +124,12 @@
     return reinterpret_cast<const uint32_t*>(Begin() + sizeof(Header))[dex_file_index];
   }
 
+  // Opens all the dex files contained in this vdex file.  This is currently
+  // used for dumping tools only, and has not been tested for use by the
+  // remainder of the runtime.
+  bool OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_files,
+                       std::string* error_msg);
+
  private:
   explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
 
diff --git a/test/901-hello-ti-agent/basics.cc b/test/901-hello-ti-agent/basics.cc
index 00776ca..20b227a 100644
--- a/test/901-hello-ti-agent/basics.cc
+++ b/test/901-hello-ti-agent/basics.cc
@@ -21,7 +21,7 @@
 #include <jni.h>
 #include <stdio.h>
 #include <string.h>
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jvmti.h"
 
 // Test infrastructure
diff --git a/test/903-hello-tagging/tagging.cc b/test/903-hello-tagging/tagging.cc
index 7f079a2..701b0c3 100644
--- a/test/903-hello-tagging/tagging.cc
+++ b/test/903-hello-tagging/tagging.cc
@@ -19,14 +19,12 @@
 #include <stdio.h>
 #include <vector>
 
+#include "android-base/logging.h"
 #include "jni.h"
-#include "ScopedLocalRef.h"
-#include "ScopedPrimitiveArray.h"
+#include "scoped_local_ref.h"
+#include "scoped_primitive_array.h"
 
-#include "art_method-inl.h"
-#include "base/logging.h"
 #include "jvmti.h"
-#include "utils.h"
 
 // Test infrastructure
 #include "jvmti_helper.h"
diff --git a/test/904-object-allocation/tracking.cc b/test/904-object-allocation/tracking.cc
index 303f954..c829496 100644
--- a/test/904-object-allocation/tracking.cc
+++ b/test/904-object-allocation/tracking.cc
@@ -19,12 +19,11 @@
 #include <stdio.h>
 #include <vector>
 
-#include "base/logging.h"
+#include "android-base/logging.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
-#include "ScopedUtfChars.h"
-#include "utils.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
 
 // Test infrastructure
 #include "jvmti_helper.h"
diff --git a/test/905-object-free/tracking_free.cc b/test/905-object-free/tracking_free.cc
index 68ce38d..59b429c 100644
--- a/test/905-object-free/tracking_free.cc
+++ b/test/905-object-free/tracking_free.cc
@@ -19,12 +19,11 @@
 #include <stdio.h>
 #include <vector>
 
-#include "base/logging.h"
+#include "android-base/logging.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
-#include "ScopedUtfChars.h"
-#include "utils.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
 
 // Test infrastructure
 #include "jvmti_helper.h"
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
index 74cb1e9..bb30074 100644
--- a/test/906-iterate-heap/iterate_heap.cc
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -23,16 +23,18 @@
 #include <stdio.h>
 #include <vector>
 
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
-#include "base/logging.h"
+
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedPrimitiveArray.h"
-#include "utf.h"
+#include "scoped_primitive_array.h"
 
 // Test infrastructure
 #include "jvmti_helper.h"
 #include "test_env.h"
+#include "ti_macros.h"
+#include "ti_utf.h"
 
 namespace art {
 namespace Test906IterateHeap {
@@ -197,10 +199,10 @@
                                             void* user_data) {
       FindStringCallbacks* p = reinterpret_cast<FindStringCallbacks*>(user_data);
       if (*tag_ptr == p->tag_to_find) {
-        size_t utf_byte_count = CountUtf8Bytes(value, value_length);
+        size_t utf_byte_count = ti::CountUtf8Bytes(value, value_length);
         std::unique_ptr<char[]> mod_utf(new char[utf_byte_count + 1]);
         memset(mod_utf.get(), 0, utf_byte_count + 1);
-        ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
+        ti::ConvertUtf16ToModifiedUtf8(mod_utf.get(), utf_byte_count, value, value_length);
         if (!p->data.empty()) {
           p->data += "\n";
         }
diff --git a/test/907-get-loaded-classes/get_loaded_classes.cc b/test/907-get-loaded-classes/get_loaded_classes.cc
index 1b973bf..5ec56c4 100644
--- a/test/907-get-loaded-classes/get_loaded_classes.cc
+++ b/test/907-get-loaded-classes/get_loaded_classes.cc
@@ -19,11 +19,12 @@
 #include <stdio.h>
 #include <vector>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
+
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
-#include "ScopedUtfChars.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
 
 // Test infrastructure
 #include "jni_helper.h"
diff --git a/test/908-gc-start-finish/gc_callbacks.cc b/test/908-gc-start-finish/gc_callbacks.cc
index 4b9a23c..f186895 100644
--- a/test/908-gc-start-finish/gc_callbacks.cc
+++ b/test/908-gc-start-finish/gc_callbacks.cc
@@ -17,7 +17,8 @@
 #include <stdio.h>
 #include <string.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
+
 #include "jni.h"
 #include "jvmti.h"
 
diff --git a/test/909-attach-agent/attach.cc b/test/909-attach-agent/attach.cc
index 67c7567..0150e09 100644
--- a/test/909-attach-agent/attach.cc
+++ b/test/909-attach-agent/attach.cc
@@ -19,7 +19,9 @@
 #include <jni.h>
 #include <stdio.h>
 #include <string.h>
-#include "base/macros.h"
+
+#include "android-base/macros.h"
+
 #include "jvmti.h"
 
 namespace art {
diff --git a/test/910-methods/methods.cc b/test/910-methods/methods.cc
index 429076c..ded4f09 100644
--- a/test/910-methods/methods.cc
+++ b/test/910-methods/methods.cc
@@ -16,10 +16,11 @@
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
+
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
 // Test infrastructure
 #include "jni_helper.h"
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index 49cbb7e..a499e90 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -18,20 +18,19 @@
 #include <memory>
 #include <stdio.h>
 
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 
-#include "android-base/stringprintf.h"
-#include "base/logging.h"
-#include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
 // Test infrastructure
 #include "jni_binder.h"
 #include "jni_helper.h"
 #include "jvmti_helper.h"
 #include "test_env.h"
+#include "ti_macros.h"
 
 namespace art {
 namespace Test911GetStackTrace {
diff --git a/test/912-classes/classes.cc b/test/912-classes/classes.cc
index 4d84e39..2636367 100644
--- a/test/912-classes/classes.cc
+++ b/test/912-classes/classes.cc
@@ -16,14 +16,15 @@
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
+
 #include "class_linker.h"
 #include "jni.h"
 #include "mirror/class_loader.h"
 #include "jvmti.h"
 #include "runtime.h"
-#include "ScopedLocalRef.h"
-#include "ScopedUtfChars.h"
+#include "scoped_local_ref.h"
+#include "scoped_utf_chars.h"
 #include "scoped_thread_state_change-inl.h"
 #include "thread-inl.h"
 
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 999467f..6a47ca1 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -21,10 +21,10 @@
 #include <iostream>
 #include <vector>
 
+#include "android-base/macros.h"
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 
-#include "base/logging.h"
-#include "base/macros.h"
 #include "jit/jit.h"
 #include "jni.h"
 #include "native_stack_dump.h"
diff --git a/test/918-fields/fields.cc b/test/918-fields/fields.cc
index 0c019e3..726c5cf 100644
--- a/test/918-fields/fields.cc
+++ b/test/918-fields/fields.cc
@@ -16,10 +16,10 @@
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
 // Test infrastructure
 #include "jni_helper.h"
diff --git a/test/920-objects/objects.cc b/test/920-objects/objects.cc
index 1dfb516..5263e75 100644
--- a/test/920-objects/objects.cc
+++ b/test/920-objects/objects.cc
@@ -16,10 +16,10 @@
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
 // Test infrastructure
 #include "test_env.h"
diff --git a/test/922-properties/properties.cc b/test/922-properties/properties.cc
index 948da6a..896e4c3 100644
--- a/test/922-properties/properties.cc
+++ b/test/922-properties/properties.cc
@@ -16,10 +16,10 @@
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedUtfChars.h"
+#include "scoped_utf_chars.h"
 
 // Test infrastructure
 #include "jni_helper.h"
diff --git a/test/923-monitors/monitors.cc b/test/923-monitors/monitors.cc
index 60d5b5a..6369a74 100644
--- a/test/923-monitors/monitors.cc
+++ b/test/923-monitors/monitors.cc
@@ -16,10 +16,10 @@
 
 #include <stdio.h>
 
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedUtfChars.h"
+#include "scoped_utf_chars.h"
 
 // Test infrastructure
 #include "jvmti_helper.h"
diff --git a/test/924-threads/threads.cc b/test/924-threads/threads.cc
index bb040bd..a8b37ec 100644
--- a/test/924-threads/threads.cc
+++ b/test/924-threads/threads.cc
@@ -16,17 +16,17 @@
 
 #include <stdio.h>
 
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
-#include "base/macros.h"
-#include "base/logging.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
 // Test infrastructure
 #include "jni_helper.h"
 #include "jvmti_helper.h"
 #include "test_env.h"
+#include "ti_macros.h"
 
 namespace art {
 namespace Test924Threads {
diff --git a/test/925-threadgroups/threadgroups.cc b/test/925-threadgroups/threadgroups.cc
index 1cd93be..d555553 100644
--- a/test/925-threadgroups/threadgroups.cc
+++ b/test/925-threadgroups/threadgroups.cc
@@ -16,17 +16,17 @@
 
 #include <stdio.h>
 
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
-#include "base/macros.h"
-#include "base/logging.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
 // Test infrastructure
 #include "jni_helper.h"
 #include "jvmti_helper.h"
 #include "test_env.h"
+#include "ti_macros.h"
 
 namespace art {
 namespace Test925ThreadGroups {
diff --git a/test/927-timers/timers.cc b/test/927-timers/timers.cc
index a67f5b4..55d3921 100644
--- a/test/927-timers/timers.cc
+++ b/test/927-timers/timers.cc
@@ -16,9 +16,9 @@
 
 #include <inttypes.h>
 
+#include "android-base/logging.h"
 #include "android-base/stringprintf.h"
-#include "base/logging.h"
-#include "base/macros.h"
+
 #include "jni.h"
 #include "jvmti.h"
 
@@ -26,6 +26,7 @@
 #include "jni_helper.h"
 #include "jvmti_helper.h"
 #include "test_env.h"
+#include "ti_macros.h"
 
 namespace art {
 namespace Test926Timers {
diff --git a/test/928-jni-table/jni_table.cc b/test/928-jni-table/jni_table.cc
index 3f4a93e..26a6707 100644
--- a/test/928-jni-table/jni_table.cc
+++ b/test/928-jni-table/jni_table.cc
@@ -19,8 +19,8 @@
 #include "jni.h"
 #include "jvmti.h"
 
-#include "base/logging.h"
-#include "base/macros.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
 
 // Test infrastructure
 #include "jvmti_helper.h"
diff --git a/test/929-search/search.cc b/test/929-search/search.cc
index bed4dfe..5516105 100644
--- a/test/929-search/search.cc
+++ b/test/929-search/search.cc
@@ -16,12 +16,12 @@
 
 #include <inttypes.h>
 
+#include "android-base/logging.h"
+#include "android-base/macros.h"
 #include "android-base/stringprintf.h"
-#include "base/logging.h"
-#include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedUtfChars.h"
+#include "scoped_utf_chars.h"
 
 // Test infrastructure
 #include "jvmti_helper.h"
diff --git a/test/931-agent-thread/agent_thread.cc b/test/931-agent-thread/agent_thread.cc
index 3ec8793..f9af8cf 100644
--- a/test/931-agent-thread/agent_thread.cc
+++ b/test/931-agent-thread/agent_thread.cc
@@ -15,17 +15,14 @@
  */
 
 #include <inttypes.h>
+#include <pthread.h>
 #include <sched.h>
 
-#include "barrier.h"
-#include "base/logging.h"
-#include "base/macros.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "runtime.h"
-#include "ScopedLocalRef.h"
-#include "thread-inl.h"
-#include "well_known_classes.h"
+#include "scoped_local_ref.h"
 
 // Test infrastructure
 #include "jvmti_helper.h"
@@ -37,13 +34,12 @@
 struct AgentData {
   AgentData() : main_thread(nullptr),
                 jvmti_env(nullptr),
-                b(2),
                 priority(0) {
   }
 
   jthread main_thread;
   jvmtiEnv* jvmti_env;
-  Barrier b;
+  pthread_barrier_t b;
   jint priority;
 };
 
@@ -54,14 +50,21 @@
   // This thread is not the main thread.
   jthread this_thread;
   jvmtiError this_thread_result = jenv->GetCurrentThread(&this_thread);
-  CHECK(!JvmtiErrorToException(env, jenv, this_thread_result));
+  CheckJvmtiError(jenv, this_thread_result);
   CHECK(!env->IsSameObject(this_thread, data->main_thread));
 
   // The thread is a daemon.
   jvmtiThreadInfo info;
   jvmtiError info_result = jenv->GetThreadInfo(this_thread, &info);
-  CHECK(!JvmtiErrorToException(env, jenv, info_result));
+  CheckJvmtiError(jenv, info_result);
   CHECK(info.is_daemon);
+  CheckJvmtiError(jenv, jenv->Deallocate(reinterpret_cast<unsigned char*>(info.name)));
+  if (info.thread_group != nullptr) {
+    env->DeleteLocalRef(info.thread_group);
+  }
+  if (info.context_class_loader != nullptr) {
+    env->DeleteLocalRef(info.context_class_loader);
+  }
 
   // The thread has the requested priority.
   // TODO: Our thread priorities do not work on the host.
@@ -71,7 +74,7 @@
   jint thread_count;
   jthread* threads;
   jvmtiError threads_result = jenv->GetAllThreads(&thread_count, &threads);
-  CHECK(!JvmtiErrorToException(env, jenv, threads_result));
+  CheckJvmtiError(jenv, threads_result);
   bool found = false;
   for (jint i = 0; i != thread_count; ++i) {
     if (env->IsSameObject(threads[i], this_thread)) {
@@ -82,29 +85,53 @@
   CHECK(found);
 
   // Done, let the main thread progress.
-  data->b.Pass(Thread::Current());
+  int wait_result = pthread_barrier_wait(&data->b);
+  CHECK(wait_result == PTHREAD_BARRIER_SERIAL_THREAD || wait_result == 0);
 }
 
 extern "C" JNIEXPORT void JNICALL Java_Main_testAgentThread(
     JNIEnv* env, jclass Main_klass ATTRIBUTE_UNUSED) {
   // Create a Thread object.
-  ScopedLocalRef<jobject> thread_name(env,
-                                      env->NewStringUTF("Agent Thread"));
+  ScopedLocalRef<jobject> thread_name(env, env->NewStringUTF("Agent Thread"));
   if (thread_name.get() == nullptr) {
     return;
   }
 
-  ScopedLocalRef<jobject> thread(env, env->AllocObject(WellKnownClasses::java_lang_Thread));
+  ScopedLocalRef<jclass> thread_klass(env, env->FindClass("java/lang/Thread"));
+  if (thread_klass.get() == nullptr) {
+    return;
+  }
+  ScopedLocalRef<jobject> thread(env, env->AllocObject(thread_klass.get()));
   if (thread.get() == nullptr) {
     return;
   }
 
+  // Get a ThreadGroup from the current thread. We need a non-null one as we're gonna call a
+  // runtime-only constructor (so we can set priority and daemon state).
+  jvmtiThreadInfo cur_thread_info;
+  jvmtiError info_result = jvmti_env->GetThreadInfo(nullptr, &cur_thread_info);
+  if (JvmtiErrorToException(env, jvmti_env, info_result)) {
+    return;
+  }
+  CheckJvmtiError(jvmti_env,
+                  jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(cur_thread_info.name)));
+  ScopedLocalRef<jobject> thread_group(env, cur_thread_info.thread_group);
+  if (cur_thread_info.context_class_loader != nullptr) {
+    env->DeleteLocalRef(cur_thread_info.context_class_loader);
+  }
+
+  jmethodID initID = env->GetMethodID(thread_klass.get(),
+                                      "<init>",
+                                      "(Ljava/lang/ThreadGroup;Ljava/lang/String;IZ)V");
+  if (initID == nullptr) {
+    return;
+  }
   env->CallNonvirtualVoidMethod(thread.get(),
-                                WellKnownClasses::java_lang_Thread,
-                                WellKnownClasses::java_lang_Thread_init,
-                                Runtime::Current()->GetMainThreadGroup(),
+                                thread_klass.get(),
+                                initID,
+                                thread_group.get(),
                                 thread_name.get(),
-                                kMinThreadPriority,
+                                0,
                                 JNI_FALSE);
   if (env->ExceptionCheck()) {
     return;
@@ -120,18 +147,20 @@
   data.main_thread = env->NewGlobalRef(main_thread);
   data.jvmti_env = jvmti_env;
   data.priority = JVMTI_THREAD_MIN_PRIORITY;
+  CHECK_EQ(0, pthread_barrier_init(&data.b, nullptr, 2));
 
   jvmtiError result = jvmti_env->RunAgentThread(thread.get(), AgentMain, &data, data.priority);
   if (JvmtiErrorToException(env, jvmti_env, result)) {
     return;
   }
 
-  data.b.Wait(Thread::Current());
+  int wait_result = pthread_barrier_wait(&data.b);
+  CHECK(wait_result == PTHREAD_BARRIER_SERIAL_THREAD || wait_result == 0);
 
   // Scheduling may mean that the agent thread is put to sleep. Wait until it's dead in an effort
   // to not unload the plugin and crash.
   for (;;) {
-    NanoSleep(1000 * 1000);
+    sleep(1);
     jint thread_state;
     jvmtiError state_result = jvmti_env->GetThreadState(thread.get(), &thread_state);
     if (JvmtiErrorToException(env, jvmti_env, state_result)) {
@@ -144,9 +173,11 @@
   }
   // Yield and sleep a bit more, to give the plugin time to tear down the native thread structure.
   sched_yield();
-  NanoSleep(100 * 1000 * 1000);
+  sleep(1);
 
   env->DeleteGlobalRef(data.main_thread);
+
+  pthread_barrier_destroy(&data.b);
 }
 
 }  // namespace Test930AgentThread
diff --git a/test/933-misc-events/misc_events.cc b/test/933-misc-events/misc_events.cc
index 7b6c64d..2b74c40 100644
--- a/test/933-misc-events/misc_events.cc
+++ b/test/933-misc-events/misc_events.cc
@@ -18,8 +18,8 @@
 #include <signal.h>
 #include <sys/types.h>
 
-#include "base/logging.h"
-#include "base/macros.h"
+#include "android-base/logging.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
 
diff --git a/test/936-search-onload/search_onload.cc b/test/936-search-onload/search_onload.cc
index 72987eb..b2ef056 100644
--- a/test/936-search-onload/search_onload.cc
+++ b/test/936-search-onload/search_onload.cc
@@ -23,7 +23,7 @@
 #include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedUtfChars.h"
+#include "scoped_utf_chars.h"
 
 // Test infrastructure
 #include "jvmti_helper.h"
diff --git a/test/944-transform-classloaders/classloader.cc b/test/944-transform-classloaders/classloader.cc
index f46763c..698e023 100644
--- a/test/944-transform-classloaders/classloader.cc
+++ b/test/944-transform-classloaders/classloader.cc
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-#include "base/macros.h"
+#include "android-base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
 #include "mirror/class-inl.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
 // Test infrastructure
 #include "test_env.h"
@@ -26,7 +26,6 @@
 namespace art {
 namespace Test944TransformClassloaders {
 
-
 extern "C" JNIEXPORT jlong JNICALL Java_Main_getDexFilePointer(JNIEnv* env, jclass, jclass klass) {
   if (Runtime::Current() == nullptr) {
     env->ThrowNew(env->FindClass("java/lang/Exception"),
diff --git a/test/945-obsolete-native/obsolete_native.cc b/test/945-obsolete-native/obsolete_native.cc
index b9303dd..ee653a4 100644
--- a/test/945-obsolete-native/obsolete_native.cc
+++ b/test/945-obsolete-native/obsolete_native.cc
@@ -25,7 +25,7 @@
 #include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedLocalRef.h"
+#include "scoped_local_ref.h"
 
 // Test infrastructure
 #include "jni_binder.h"
diff --git a/test/980-redefine-object/redefine_object.cc b/test/980-redefine-object/redefine_object.cc
index 6c8c4bd..1faf1a1 100644
--- a/test/980-redefine-object/redefine_object.cc
+++ b/test/980-redefine-object/redefine_object.cc
@@ -22,7 +22,7 @@
 #include "base/macros.h"
 #include "jni.h"
 #include "jvmti.h"
-#include "ScopedUtfChars.h"
+#include "scoped_utf_chars.h"
 
 // Test infrastructure
 #include "jni_binder.h"
diff --git a/test/Android.bp b/test/Android.bp
index 2e8f5bb..40f7edd 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -241,29 +241,24 @@
 }
 
 art_cc_defaults {
-    name: "libtiagent-defaults",
+   name: "libtiagent-base-defaults",
     defaults: ["libartagent-defaults"],
     srcs: [
-        // This is to get the IsInterpreted native method.
-        "common/stack_inspect.cc",
-        "common/runtime_state.cc",
-        "ti-agent/common_load.cc",
-        "ti-agent/common_helper.cc",
+        // These are the ART-independent parts.
+        "ti-agent/agent_startup.cc",
         "ti-agent/jni_binder.cc",
         "ti-agent/jvmti_helper.cc",
         "ti-agent/test_env.cc",
-        "901-hello-ti-agent/basics.cc",
+        // This is the list of non-special OnLoad things and excludes BCI and anything that depends
+        // on ART internals.
         "903-hello-tagging/tagging.cc",
         "904-object-allocation/tracking.cc",
         "905-object-free/tracking_free.cc",
         "906-iterate-heap/iterate_heap.cc",
         "907-get-loaded-classes/get_loaded_classes.cc",
         "908-gc-start-finish/gc_callbacks.cc",
-        "909-attach-agent/attach.cc",
         "910-methods/methods.cc",
         "911-get-stack-trace/stack_trace.cc",
-        "912-classes/classes.cc",
-        "913-heaps/heaps.cc",
         "918-fields/fields.cc",
         "920-objects/objects.cc",
         "922-properties/properties.cc",
@@ -275,11 +270,6 @@
         "929-search/search.cc",
         "931-agent-thread/agent_thread.cc",
         "933-misc-events/misc_events.cc",
-        "936-search-onload/search_onload.cc",
-        "944-transform-classloaders/classloader.cc",
-        "945-obsolete-native/obsolete_native.cc",
-        "980-redefine-object/redefine_object.cc",
-        "983-source-transform-verify/source_transform.cc",
     ],
     shared_libs: [
         "libbase",
@@ -288,6 +278,29 @@
     include_dirs: ["art/test/ti-agent"],
 }
 
+art_cc_defaults {
+    name: "libtiagent-defaults",
+    defaults: ["libtiagent-base-defaults"],
+    srcs: [
+        // This is to get the IsInterpreted native method.
+        "common/stack_inspect.cc",
+        "common/runtime_state.cc",
+        // This includes the remaining test functions. We should try to refactor things to
+        // make this list smaller.
+        "ti-agent/common_helper.cc",
+        "ti-agent/common_load.cc",
+        "901-hello-ti-agent/basics.cc",
+        "909-attach-agent/attach.cc",
+        "912-classes/classes.cc",
+        "913-heaps/heaps.cc",
+        "936-search-onload/search_onload.cc",
+        "944-transform-classloaders/classloader.cc",
+        "945-obsolete-native/obsolete_native.cc",
+        "980-redefine-object/redefine_object.cc",
+        "983-source-transform-verify/source_transform.cc",
+    ],
+}
+
 art_cc_test_library {
     name: "libtiagent",
     defaults: ["libtiagent-defaults"],
@@ -303,6 +316,12 @@
     shared_libs: ["libartd"],
 }
 
+art_cc_test_library {
+    name: "libctstiagent",
+    defaults: ["libtiagent-base-defaults"],
+    export_include_dirs: ["ti-agent"],
+}
+
 cc_defaults {
     name: "libarttest-defaults",
     defaults: [
diff --git a/test/ti-agent/agent_startup.cc b/test/ti-agent/agent_startup.cc
new file mode 100644
index 0000000..b55db7b
--- /dev/null
+++ b/test/ti-agent/agent_startup.cc
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ */
+
+#include "agent_startup.h"
+
+#include "android-base/logging.h"
+#include "android-base/macros.h"
+
+#include "jni_binder.h"
+#include "jvmti_helper.h"
+
+namespace art {
+
+static constexpr const char* kMainClass = "Main";
+
+static StartCallback gCallback = nullptr;
+
+// TODO: Check this. This may not work on device. The classloader containing the app's classes
+//       may not have been created at this point (i.e., if it's not the system classloader).
+static void JNICALL VMInitCallback(jvmtiEnv* jvmti_env,
+                                   JNIEnv* jni_env,
+                                   jthread thread ATTRIBUTE_UNUSED) {
+  // Bind kMainClass native methods.
+  BindFunctions(jvmti_env, jni_env, kMainClass);
+
+  if (gCallback != nullptr) {
+    gCallback(jvmti_env, jni_env);
+    gCallback = nullptr;
+  }
+
+  // And delete the jvmtiEnv.
+  jvmti_env->DisposeEnvironment();
+}
+
+// Install a phase callback that will bind JNI functions on VMInit.
+void BindOnLoad(JavaVM* vm, StartCallback callback) {
+  // Use a new jvmtiEnv. Otherwise we might collide with table changes.
+  jvmtiEnv* install_env;
+  if (vm->GetEnv(reinterpret_cast<void**>(&install_env), JVMTI_VERSION_1_0) != 0) {
+    LOG(FATAL) << "Could not get jvmtiEnv";
+  }
+  SetAllCapabilities(install_env);
+
+  {
+    jvmtiEventCallbacks callbacks;
+    memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
+    callbacks.VMInit = VMInitCallback;
+
+    CheckJvmtiError(install_env, install_env->SetEventCallbacks(&callbacks, sizeof(callbacks)));
+  }
+
+  CheckJvmtiError(install_env, install_env->SetEventNotificationMode(JVMTI_ENABLE,
+                                                                     JVMTI_EVENT_VM_INIT,
+                                                                     nullptr));
+
+  gCallback = callback;
+}
+
+// Ensure binding of the Main class when the agent is started through OnAttach.
+void BindOnAttach(JavaVM* vm, StartCallback callback) {
+  // Get a JNIEnv. As the thread is attached, we must not destroy it.
+  JNIEnv* env;
+  CHECK_EQ(0, vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6))
+      << "Could not get JNIEnv";
+
+  jvmtiEnv* jvmti_env;
+  CHECK_EQ(0, vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0))
+      << "Could not get jvmtiEnv";
+  SetAllCapabilities(jvmti_env);
+
+  BindFunctions(jvmti_env, env, kMainClass);
+
+  if (callback != nullptr) {
+    callback(jvmti_env, env);
+  }
+
+  if (jvmti_env->DisposeEnvironment() != JVMTI_ERROR_NONE) {
+    LOG(FATAL) << "Could not dispose temporary jvmtiEnv";
+  }
+}
+
+}  // namespace art
diff --git a/test/ti-agent/agent_startup.h b/test/ti-agent/agent_startup.h
new file mode 100644
index 0000000..4963320
--- /dev/null
+++ b/test/ti-agent/agent_startup.h
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_TEST_TI_AGENT_AGENT_STARTUP_H_
+#define ART_TEST_TI_AGENT_AGENT_STARTUP_H_
+
+#include <functional>
+
+#include "jni.h"
+#include "jvmti.h"
+
+namespace art {
+
+using StartCallback = void(*)(jvmtiEnv*, JNIEnv*);
+
+// Ensure binding of the Main class when the agent is started through OnLoad.
+void BindOnLoad(JavaVM* vm, StartCallback callback);
+
+// Ensure binding of the Main class when the agent is started through OnAttach.
+void BindOnAttach(JavaVM* vm, StartCallback callback);
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_AGENT_STARTUP_H_
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index c5d75c9..9e7b75d 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -19,6 +19,8 @@
 
 #include "base/logging.h"
 #include "base/macros.h"
+
+#include "agent_startup.h"
 #include "common_helper.h"
 #include "jni_binder.h"
 #include "jvmti_helper.h"
@@ -42,45 +44,6 @@
   OnAttach attach;
 };
 
-static void JNICALL VMInitCallback(jvmtiEnv *jvmti_env,
-                                   JNIEnv* jni_env,
-                                   jthread thread ATTRIBUTE_UNUSED) {
-  // Bind Main native methods.
-  BindFunctions(jvmti_env, jni_env, "Main");
-}
-
-// Install a phase callback that will bind JNI functions on VMInit.
-bool InstallBindCallback(JavaVM* vm) {
-  // Use a new jvmtiEnv. Otherwise we might collide with table changes.
-  jvmtiEnv* install_env;
-  if (vm->GetEnv(reinterpret_cast<void**>(&install_env), JVMTI_VERSION_1_0) != 0) {
-    return false;
-  }
-  SetAllCapabilities(install_env);
-
-  {
-    jvmtiEventCallbacks callbacks;
-    memset(&callbacks, 0, sizeof(jvmtiEventCallbacks));
-    callbacks.VMInit = VMInitCallback;
-
-    jvmtiError install_error = install_env->SetEventCallbacks(&callbacks, sizeof(callbacks));
-    if (install_error != JVMTI_ERROR_NONE) {
-      return false;
-    }
-  }
-
-  {
-    jvmtiError enable_error = install_env->SetEventNotificationMode(JVMTI_ENABLE,
-                                                                    JVMTI_EVENT_VM_INIT,
-                                                                    nullptr);
-    if (enable_error != JVMTI_ERROR_NONE) {
-      return false;
-    }
-  }
-
-  return true;
-}
-
 // A trivial OnLoad implementation that only initializes the global jvmti_env.
 static jint MinimalOnLoad(JavaVM* vm,
                           char* options ATTRIBUTE_UNUSED,
@@ -156,26 +119,6 @@
   SetJVM(strncmp(options, "jvm", 3) == 0);
 }
 
-static bool BindFunctionsAttached(JavaVM* vm, const char* class_name) {
-  // Get a JNIEnv. As the thread is attached, we must not destroy it.
-  JNIEnv* env;
-  if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != 0) {
-    printf("Unable to get JNI env!\n");
-    return false;
-  }
-
-  jvmtiEnv* jenv;
-  if (vm->GetEnv(reinterpret_cast<void**>(&jenv), JVMTI_VERSION_1_0) != 0) {
-    printf("Unable to get jvmti env!\n");
-    return false;
-  }
-  SetAllCapabilities(jenv);
-
-  BindFunctions(jenv, env, class_name);
-
-  return true;
-}
-
 }  // namespace
 
 extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
@@ -188,9 +131,7 @@
 
   SetIsJVM(remaining_options);
 
-  if (!InstallBindCallback(vm)) {
-    return 1;
-  }
+  BindOnLoad(vm, nullptr);
 
   AgentLib* lib = FindAgent(name_option);
   OnLoad fn = nullptr;
@@ -214,7 +155,7 @@
     return -1;
   }
 
-  BindFunctionsAttached(vm, "Main");
+  BindOnAttach(vm, nullptr);
 
   AgentLib* lib = FindAgent(name_option);
   if (lib == nullptr) {
diff --git a/test/ti-agent/jni_binder.cc b/test/ti-agent/jni_binder.cc
index efc2af8..b66c2c7 100644
--- a/test/ti-agent/jni_binder.cc
+++ b/test/ti-agent/jni_binder.cc
@@ -26,94 +26,16 @@
 #include "jvmti_helper.h"
 #include "scoped_local_ref.h"
 #include "scoped_utf_chars.h"
+#include "ti_utf.h"
 
 namespace art {
 
-size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count) {
-  DCHECK_LE(byte_count, strlen(utf8));
-  size_t len = 0;
-  const char* end = utf8 + byte_count;
-  for (; utf8 < end; ++utf8) {
-    int ic = *utf8;
-    len++;
-    if (LIKELY((ic & 0x80) == 0)) {
-      // One-byte encoding.
-      continue;
-    }
-    // Two- or three-byte encoding.
-    utf8++;
-    if ((ic & 0x20) == 0) {
-      // Two-byte encoding.
-      continue;
-    }
-    utf8++;
-    if ((ic & 0x10) == 0) {
-      // Three-byte encoding.
-      continue;
-    }
-
-    // Four-byte encoding: needs to be converted into a surrogate
-    // pair.
-    utf8++;
-    len++;
-  }
-  return len;
-}
-
-static uint16_t GetTrailingUtf16Char(uint32_t maybe_pair) {
-  return static_cast<uint16_t>(maybe_pair >> 16);
-}
-
-static uint16_t GetLeadingUtf16Char(uint32_t maybe_pair) {
-  return static_cast<uint16_t>(maybe_pair & 0x0000FFFF);
-}
-
-static uint32_t GetUtf16FromUtf8(const char** utf8_data_in) {
-  const uint8_t one = *(*utf8_data_in)++;
-  if ((one & 0x80) == 0) {
-    // one-byte encoding
-    return one;
-  }
-
-  const uint8_t two = *(*utf8_data_in)++;
-  if ((one & 0x20) == 0) {
-    // two-byte encoding
-    return ((one & 0x1f) << 6) | (two & 0x3f);
-  }
-
-  const uint8_t three = *(*utf8_data_in)++;
-  if ((one & 0x10) == 0) {
-    return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
-  }
-
-  // Four byte encodings need special handling. We'll have
-  // to convert them into a surrogate pair.
-  const uint8_t four = *(*utf8_data_in)++;
-
-  // Since this is a 4 byte UTF-8 sequence, it will lie between
-  // U+10000 and U+1FFFFF.
-  //
-  // TODO: What do we do about values in (U+10FFFF, U+1FFFFF) ? The
-  // spec says they're invalid but nobody appears to check for them.
-  const uint32_t code_point = ((one & 0x0f) << 18) | ((two & 0x3f) << 12)
-      | ((three & 0x3f) << 6) | (four & 0x3f);
-
-  uint32_t surrogate_pair = 0;
-  // Step two: Write out the high (leading) surrogate to the bottom 16 bits
-  // of the of the 32 bit type.
-  surrogate_pair |= ((code_point >> 10) + 0xd7c0) & 0xffff;
-  // Step three : Write out the low (trailing) surrogate to the top 16 bits.
-  surrogate_pair |= ((code_point & 0x03ff) + 0xdc00) << 16;
-
-  return surrogate_pair;
-}
-
 static std::string MangleForJni(const std::string& s) {
   std::string result;
-  size_t char_count = CountModifiedUtf8Chars(s.c_str(), s.length());
+  size_t char_count = ti::CountModifiedUtf8Chars(s.c_str(), s.length());
   const char* cp = &s[0];
   for (size_t i = 0; i < char_count; ++i) {
-    uint32_t ch = GetUtf16FromUtf8(&cp);
+    uint32_t ch = ti::GetUtf16FromUtf8(&cp);
     if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
       result.push_back(ch);
     } else if (ch == '.' || ch == '/') {
@@ -125,8 +47,8 @@
     } else if (ch == '[') {
       result += "_3";
     } else {
-      const uint16_t leading = GetLeadingUtf16Char(ch);
-      const uint32_t trailing = GetTrailingUtf16Char(ch);
+      const uint16_t leading = ti::GetLeadingUtf16Char(ch);
+      const uint32_t trailing = ti::GetTrailingUtf16Char(ch);
 
       android::base::StringAppendF(&result, "_0%04x", leading);
       if (trailing != 0) {
diff --git a/test/ti-agent/jni_helper.h b/test/ti-agent/jni_helper.h
index c48b0c0..0cbc634 100644
--- a/test/ti-agent/jni_helper.h
+++ b/test/ti-agent/jni_helper.h
@@ -54,6 +54,19 @@
   return ret.release();
 }
 
+inline bool JniThrowNullPointerException(JNIEnv* env, const char* msg) {
+  if (env->ExceptionCheck()) {
+    env->ExceptionClear();
+  }
+
+  ScopedLocalRef<jclass> exc_class(env, env->FindClass("java/lang/NullPointerException"));
+  if (exc_class.get() == nullptr) {
+    return -1;
+  }
+
+  return env->ThrowNew(exc_class.get(), msg) == JNI_OK;
+}
+
 }  // namespace art
 
 #endif  // ART_TEST_TI_AGENT_JNI_HELPER_H_
diff --git a/test/ti-agent/scoped_local_ref.h b/test/ti-agent/scoped_local_ref.h
index 0cd9891..daa1583 100644
--- a/test/ti-agent/scoped_local_ref.h
+++ b/test/ti-agent/scoped_local_ref.h
@@ -35,9 +35,9 @@
     reset();
   }
 
-  void reset(T ptr = NULL) {
+  void reset(T ptr = nullptr) {
     if (ptr != mLocalRef) {
-      if (mLocalRef != NULL) {
+      if (mLocalRef != nullptr) {
         mEnv->DeleteLocalRef(mLocalRef);
       }
       mLocalRef = ptr;
@@ -46,7 +46,7 @@
 
   T release() __attribute__((warn_unused_result)) {
     T localRef = mLocalRef;
-    mLocalRef = NULL;
+    mLocalRef = nullptr;
     return localRef;
   }
 
diff --git a/test/ti-agent/scoped_primitive_array.h b/test/ti-agent/scoped_primitive_array.h
new file mode 100644
index 0000000..1649ed9
--- /dev/null
+++ b/test/ti-agent/scoped_primitive_array.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2010 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_TEST_TI_AGENT_SCOPED_PRIMITIVE_ARRAY_H_
+#define ART_TEST_TI_AGENT_SCOPED_PRIMITIVE_ARRAY_H_
+
+#include "jni.h"
+
+#include "android-base/macros.h"
+
+#include "jni_helper.h"
+
+namespace art {
+
+#ifdef POINTER_TYPE
+#error POINTER_TYPE is defined.
+#else
+#define POINTER_TYPE(T) T*  /* NOLINT */
+#endif
+
+#ifdef REFERENCE_TYPE
+#error REFERENCE_TYPE is defined.
+#else
+#define REFERENCE_TYPE(T) T&  /* NOLINT */
+#endif
+
+// ScopedBooleanArrayRO, ScopedByteArrayRO, ScopedCharArrayRO, ScopedDoubleArrayRO,
+// ScopedFloatArrayRO, ScopedIntArrayRO, ScopedLongArrayRO, and ScopedShortArrayRO provide
+// convenient read-only access to Java arrays from JNI code. This is cheaper than read-write
+// access and should be used by default.
+#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(PRIMITIVE_TYPE, NAME) \
+    class Scoped ## NAME ## ArrayRO { \
+    public: \
+        explicit Scoped ## NAME ## ArrayRO(JNIEnv* env) \
+        : mEnv(env), mJavaArray(nullptr), mRawArray(nullptr), mSize(0) {} \
+        Scoped ## NAME ## ArrayRO(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
+        : mEnv(env) { \
+            if (javaArray == nullptr) { \
+                mJavaArray = nullptr; \
+                mSize = 0; \
+                mRawArray = nullptr; \
+                JniThrowNullPointerException(env, nullptr); \
+            } else { \
+                reset(javaArray); \
+            } \
+        } \
+        ~Scoped ## NAME ## ArrayRO() { \
+            if (mRawArray != nullptr && mRawArray != mBuffer) { \
+                mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, JNI_ABORT); \
+            } \
+        } \
+        void reset(PRIMITIVE_TYPE ## Array javaArray) { \
+            mJavaArray = javaArray; \
+            mSize = mEnv->GetArrayLength(mJavaArray); \
+            if (mSize <= kBufferSize) { \
+                mEnv->Get ## NAME ## ArrayRegion(mJavaArray, 0, mSize, mBuffer); \
+                mRawArray = mBuffer; \
+            } else { \
+                mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \
+            } \
+        } \
+        const PRIMITIVE_TYPE* get() const { return mRawArray; } \
+        PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
+        const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
+        size_t size() const { return mSize; } \
+    private: \
+        static constexpr jsize kBufferSize = 1024; \
+        JNIEnv* const mEnv; \
+        PRIMITIVE_TYPE ## Array mJavaArray; \
+        POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \
+        jsize mSize; \
+        PRIMITIVE_TYPE mBuffer[kBufferSize]; \
+        DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRO); \
+    }
+
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jboolean, Boolean);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jbyte, Byte);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jchar, Char);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jdouble, Double);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jfloat, Float);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jint, Int);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jlong, Long);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO(jshort, Short);
+
+#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RO
+
+// ScopedBooleanArrayRW, ScopedByteArrayRW, ScopedCharArrayRW, ScopedDoubleArrayRW,
+// ScopedFloatArrayRW, ScopedIntArrayRW, ScopedLongArrayRW, and ScopedShortArrayRW provide
+// convenient read-write access to Java arrays from JNI code. These are more expensive,
+// since they entail a copy back onto the Java heap, and should only be used when necessary.
+#define INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(PRIMITIVE_TYPE, NAME) \
+    class Scoped ## NAME ## ArrayRW { \
+    public: \
+        explicit Scoped ## NAME ## ArrayRW(JNIEnv* env) \
+        : mEnv(env), mJavaArray(nullptr), mRawArray(nullptr) {} \
+        Scoped ## NAME ## ArrayRW(JNIEnv* env, PRIMITIVE_TYPE ## Array javaArray) \
+        : mEnv(env), mJavaArray(javaArray), mRawArray(nullptr) { \
+            if (mJavaArray == nullptr) { \
+                JniThrowNullPointerException(env, nullptr); \
+            } else { \
+                mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \
+            } \
+        } \
+        ~Scoped ## NAME ## ArrayRW() { \
+            if (mRawArray) { \
+                mEnv->Release ## NAME ## ArrayElements(mJavaArray, mRawArray, 0); \
+            } \
+        } \
+        void reset(PRIMITIVE_TYPE ## Array javaArray) { \
+            mJavaArray = javaArray; \
+            mRawArray = mEnv->Get ## NAME ## ArrayElements(mJavaArray, nullptr); \
+        } \
+        const PRIMITIVE_TYPE* get() const { return mRawArray; } \
+        PRIMITIVE_TYPE ## Array getJavaArray() const { return mJavaArray; } \
+        const PRIMITIVE_TYPE& operator[](size_t n) const { return mRawArray[n]; } \
+        POINTER_TYPE(PRIMITIVE_TYPE) get() { return mRawArray; }  \
+        REFERENCE_TYPE(PRIMITIVE_TYPE) operator[](size_t n) { return mRawArray[n]; } \
+        size_t size() const { return mEnv->GetArrayLength(mJavaArray); } \
+    private: \
+        JNIEnv* const mEnv; \
+        PRIMITIVE_TYPE ## Array mJavaArray; \
+        POINTER_TYPE(PRIMITIVE_TYPE) mRawArray; \
+        DISALLOW_COPY_AND_ASSIGN(Scoped ## NAME ## ArrayRW); \
+    }
+
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jboolean, Boolean);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jbyte, Byte);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jchar, Char);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jdouble, Double);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jfloat, Float);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jint, Int);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jlong, Long);
+INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW(jshort, Short);
+
+#undef INSTANTIATE_SCOPED_PRIMITIVE_ARRAY_RW
+#undef POINTER_TYPE
+#undef REFERENCE_TYPE
+
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_SCOPED_PRIMITIVE_ARRAY_H_
diff --git a/test/ti-agent/scoped_utf_chars.h b/test/ti-agent/scoped_utf_chars.h
index e8c9a11..422caaf 100644
--- a/test/ti-agent/scoped_utf_chars.h
+++ b/test/ti-agent/scoped_utf_chars.h
@@ -23,6 +23,8 @@
 
 #include "android-base/macros.h"
 
+#include "jni_helper.h"
+
 namespace art {
 
 class ScopedUtfChars {
@@ -30,7 +32,7 @@
   ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
     if (s == nullptr) {
       utf_chars_ = nullptr;
-      // TODO: JniThrowNullPointerException(env, nullptr);
+      JniThrowNullPointerException(env, nullptr);
     } else {
       utf_chars_ = env->GetStringUTFChars(s, nullptr);
     }
diff --git a/test/ti-agent/ti_macros.h b/test/ti-agent/ti_macros.h
new file mode 100644
index 0000000..d913383
--- /dev/null
+++ b/test/ti-agent/ti_macros.h
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_TEST_TI_AGENT_TI_MACROS_H_
+#define ART_TEST_TI_AGENT_TI_MACROS_H_
+
+#include "android-base/macros.h"
+
+#define FINAL final
+#define OVERRIDE override
+#define UNREACHABLE  __builtin_unreachable
+
+#endif  // ART_TEST_TI_AGENT_TI_MACROS_H_
diff --git a/test/ti-agent/ti_utf.h b/test/ti-agent/ti_utf.h
new file mode 100644
index 0000000..341e106
--- /dev/null
+++ b/test/ti-agent/ti_utf.h
@@ -0,0 +1,196 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_TEST_TI_AGENT_TI_UTF_H_
+#define ART_TEST_TI_AGENT_TI_UTF_H_
+
+#include <inttypes.h>
+#include <string.h>
+
+#include "android-base/logging.h"
+
+namespace art {
+namespace ti {
+
+inline size_t CountModifiedUtf8Chars(const char* utf8, size_t byte_count) {
+  DCHECK_LE(byte_count, strlen(utf8));
+  size_t len = 0;
+  const char* end = utf8 + byte_count;
+  for (; utf8 < end; ++utf8) {
+    int ic = *utf8;
+    len++;
+    if (LIKELY((ic & 0x80) == 0)) {
+      // One-byte encoding.
+      continue;
+    }
+    // Two- or three-byte encoding.
+    utf8++;
+    if ((ic & 0x20) == 0) {
+      // Two-byte encoding.
+      continue;
+    }
+    utf8++;
+    if ((ic & 0x10) == 0) {
+      // Three-byte encoding.
+      continue;
+    }
+
+    // Four-byte encoding: needs to be converted into a surrogate
+    // pair.
+    utf8++;
+    len++;
+  }
+  return len;
+}
+
+inline uint16_t GetTrailingUtf16Char(uint32_t maybe_pair) {
+  return static_cast<uint16_t>(maybe_pair >> 16);
+}
+
+inline uint16_t GetLeadingUtf16Char(uint32_t maybe_pair) {
+  return static_cast<uint16_t>(maybe_pair & 0x0000FFFF);
+}
+
+inline uint32_t GetUtf16FromUtf8(const char** utf8_data_in) {
+  const uint8_t one = *(*utf8_data_in)++;
+  if ((one & 0x80) == 0) {
+    // one-byte encoding
+    return one;
+  }
+
+  const uint8_t two = *(*utf8_data_in)++;
+  if ((one & 0x20) == 0) {
+    // two-byte encoding
+    return ((one & 0x1f) << 6) | (two & 0x3f);
+  }
+
+  const uint8_t three = *(*utf8_data_in)++;
+  if ((one & 0x10) == 0) {
+    return ((one & 0x0f) << 12) | ((two & 0x3f) << 6) | (three & 0x3f);
+  }
+
+  // Four byte encodings need special handling. We'll have
+  // to convert them into a surrogate pair.
+  const uint8_t four = *(*utf8_data_in)++;
+
+  // Since this is a 4 byte UTF-8 sequence, it will lie between
+  // U+10000 and U+1FFFFF.
+  //
+  // TODO: What do we do about values in (U+10FFFF, U+1FFFFF) ? The
+  // spec says they're invalid but nobody appears to check for them.
+  const uint32_t code_point = ((one & 0x0f) << 18) | ((two & 0x3f) << 12)
+      | ((three & 0x3f) << 6) | (four & 0x3f);
+
+  uint32_t surrogate_pair = 0;
+  // Step two: Write out the high (leading) surrogate to the bottom 16 bits
+  // of the of the 32 bit type.
+  surrogate_pair |= ((code_point >> 10) + 0xd7c0) & 0xffff;
+  // Step three : Write out the low (trailing) surrogate to the top 16 bits.
+  surrogate_pair |= ((code_point & 0x03ff) + 0xdc00) << 16;
+
+  return surrogate_pair;
+}
+
+inline void ConvertUtf16ToModifiedUtf8(char* utf8_out,
+                                       size_t byte_count,
+                                       const uint16_t* utf16_in,
+                                       size_t char_count) {
+  if (LIKELY(byte_count == char_count)) {
+    // Common case where all characters are ASCII.
+    const uint16_t *utf16_end = utf16_in + char_count;
+    for (const uint16_t *p = utf16_in; p < utf16_end;) {
+      *utf8_out++ = static_cast<char>(*p++);
+    }
+    return;
+  }
+
+  // String contains non-ASCII characters.
+  while (char_count--) {
+    const uint16_t ch = *utf16_in++;
+    if (ch > 0 && ch <= 0x7f) {
+      *utf8_out++ = ch;
+    } else {
+      // Char_count == 0 here implies we've encountered an unpaired
+      // surrogate and we have no choice but to encode it as 3-byte UTF
+      // sequence. Note that unpaired surrogates can occur as a part of
+      // "normal" operation.
+      if ((ch >= 0xd800 && ch <= 0xdbff) && (char_count > 0)) {
+        const uint16_t ch2 = *utf16_in;
+
+        // Check if the other half of the pair is within the expected
+        // range. If it isn't, we will have to emit both "halves" as
+        // separate 3 byte sequences.
+        if (ch2 >= 0xdc00 && ch2 <= 0xdfff) {
+          utf16_in++;
+          char_count--;
+          const uint32_t code_point = (ch << 10) + ch2 - 0x035fdc00;
+          *utf8_out++ = (code_point >> 18) | 0xf0;
+          *utf8_out++ = ((code_point >> 12) & 0x3f) | 0x80;
+          *utf8_out++ = ((code_point >> 6) & 0x3f) | 0x80;
+          *utf8_out++ = (code_point & 0x3f) | 0x80;
+          continue;
+        }
+      }
+
+      if (ch > 0x07ff) {
+        // Three byte encoding.
+        *utf8_out++ = (ch >> 12) | 0xe0;
+        *utf8_out++ = ((ch >> 6) & 0x3f) | 0x80;
+        *utf8_out++ = (ch & 0x3f) | 0x80;
+      } else /*(ch > 0x7f || ch == 0)*/ {
+        // Two byte encoding.
+        *utf8_out++ = (ch >> 6) | 0xc0;
+        *utf8_out++ = (ch & 0x3f) | 0x80;
+      }
+    }
+  }
+}
+
+inline size_t CountUtf8Bytes(const uint16_t* chars, size_t char_count) {
+  size_t result = 0;
+  const uint16_t *end = chars + char_count;
+  while (chars < end) {
+    const uint16_t ch = *chars++;
+    if (LIKELY(ch != 0 && ch < 0x80)) {
+      result++;
+      continue;
+    }
+    if (ch < 0x800) {
+      result += 2;
+      continue;
+    }
+    if (ch >= 0xd800 && ch < 0xdc00) {
+      if (chars < end) {
+        const uint16_t ch2 = *chars;
+        // If we find a properly paired surrogate, we emit it as a 4 byte
+        // UTF sequence. If we find an unpaired leading or trailing surrogate,
+        // we emit it as a 3 byte sequence like would have done earlier.
+        if (ch2 >= 0xdc00 && ch2 < 0xe000) {
+          chars++;
+          result += 4;
+          continue;
+        }
+      }
+    }
+    result += 3;
+  }
+  return result;
+}
+
+}  // namespace ti
+}  // namespace art
+
+#endif  // ART_TEST_TI_AGENT_TI_UTF_H_
diff --git a/tools/art b/tools/art
index 6e62ad1..933ad7a 100644
--- a/tools/art
+++ b/tools/art
@@ -230,8 +230,8 @@
   replace_compiler_filter_with_interepret_only "$@"
 
   run_art -Xjitsaveprofilinginfo               \
-          -Xps-min-methods-to-save:0           \
-          -Xps-min-classes-to-save:0           \
+          -Xps-min-methods-to-save:1           \
+          -Xps-min-classes-to-save:1           \
           -Xps-min-notification-before-wake:10 \
           -Xps-profile-path:$PROFILE_PATH      \
           -Xusejit:true                        \
diff --git a/tools/cpplint_presubmit.py b/tools/cpplint_presubmit.py
new file mode 100755
index 0000000..4781517
--- /dev/null
+++ b/tools/cpplint_presubmit.py
@@ -0,0 +1,67 @@
+#!/usr/bin/python3
+#
+# Copyright 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.
+
+# TODO We should unify this with build/Android.cpplint.mk.
+
+import os
+import pathlib
+import subprocess
+import sys
+
+IGNORED_FILES = {"runtime/elf.h", "runtime/openjdkjvmti/include/jvmti.h"}
+
+INTERESTING_SUFFIXES = {".h", ".cc"}
+
+CPPLINT_FLAGS = [
+    '--filter=-whitespace/line_length,-build/include,-readability/function,-readability/streams,-readability/todo,-runtime/references,-runtime/sizeof,-runtime/threadsafe_fn,-runtime/printf',
+    '--quiet',
+]
+
+def is_interesting(f):
+  """
+  Returns true if this is a file we want to run through cpplint before uploading. False otherwise.
+  """
+  path = pathlib.Path(f)
+  return f not in IGNORED_FILES and path.suffix in INTERESTING_SUFFIXES and path.exists()
+
+def get_changed_files(commit):
+  """
+  Gets the files changed in the given commit.
+  """
+  return subprocess.check_output(
+      ["git", 'diff-tree', '--no-commit-id', '--name-only', '-r', commit],
+      stderr=subprocess.STDOUT,
+      universal_newlines=True).split()
+
+def run_cpplint(files):
+  """
+  Runs cpplint on the given files.
+  """
+  if len(files) == 0:
+    return
+  sys.exit(subprocess.call(['tools/cpplint.py'] + CPPLINT_FLAGS + files))
+
+def main():
+  if 'PREUPLOAD_COMMIT' in os.environ:
+    commit = os.environ['PREUPLOAD_COMMIT']
+  else:
+    print("WARNING: Not running as a pre-upload hook. Assuming commit to check = 'HEAD'")
+    commit = "HEAD"
+  files_to_check = [f for f in get_changed_files(commit) if is_interesting(f)]
+  run_cpplint(files_to_check)
+
+if __name__ == '__main__':
+  main()