Add oatopt drop in replacement for dexopt

Change-Id: I094375230af2d9a88e30245b390cac71be7b50f4
diff --git a/Android.mk b/Android.mk
index 06b7ebe..5b1d13b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -186,12 +186,15 @@
 zygote-artd-target-sync: $(ART_TARGET_DEPENDENCIES) $(TARGET_BOOT_OAT) $(ART_CACHE_OATS)
 	cp $(TARGET_OUT_SHARED_LIBRARIES)/libartd.so $(TARGET_OUT_SHARED_LIBRARIES)/libdvm.so
 	cp $(TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED)/libartd.so $(TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED)/libdvm.so
+	cp $(TARGET_OUT_EXECUTABLES)/oatoptd $(TARGET_OUT_EXECUTABLES)/dexopt
+	cp $(TARGET_OUT_EXECUTABLES_UNSTRIPPED)/oatoptd $(TARGET_OUT_EXECUTABLES_UNSTRIPPED)/dexopt
 	adb remount
 	adb sync
 
 .PHONY: zygote-artd
 zygote-artd: zygote-artd-target-sync
-	sed 's/--start-system-server/--start-system-server --no-preload/' < system/core/rootdir/init.rc > $(ANDROID_PRODUCT_OUT)/root/init.rc
+	sed -e 's/--start-system-server/--start-system-server --no-preload/' -e 's/art-cache 0771/art-cache 0777/' < system/core/rootdir/init.rc > $(ANDROID_PRODUCT_OUT)/root/init.rc
+	adb shell rm -f $(ART_CACHE_DIR)
 	rm -f $(ANDROID_PRODUCT_OUT)/boot.img
 	unset ONE_SHOT_MAKEFILE && $(MAKE) showcommands bootimage
 	adb reboot bootloader
@@ -202,6 +205,8 @@
 zygote-dalvik:
 	cp $(TARGET_OUT_INTERMEDIATE_LIBRARIES)/libdvm.so $(TARGET_OUT_SHARED_LIBRARIES)/libdvm.so
 	cp $(call intermediates-dir-for,SHARED_LIBRARIES,libdvm)/LINKED/libdvm.so $(TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED)/libdvm.so
+	cp $(call intermediates-dir-for,EXECUTABLES,dexopt)/dexopt $(TARGET_OUT_EXECUTABLES)/dexopt
+	cp $(call intermediates-dir-for,EXECUTABLES,dexopt)/LINKED/dexopt $(TARGET_OUT_EXECUTABLES_UNSTRIPPED)/dexopt
 	adb remount
 	adb sync
 	cp system/core/rootdir/init.rc $(ANDROID_PRODUCT_OUT)/root/init.rc
diff --git a/build/Android.common.mk b/build/Android.common.mk
index 7875ee9..319d673 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -67,6 +67,9 @@
 OATEXEC_SRC_FILES := \
 	src/oatexec.cc
 
+OATOPT_SRC_FILES := \
+	src/oatopt.cc
+
 LIBART_COMMON_SRC_FILES := \
 	src/assembler.cc \
 	src/assembler_arm.cc \
diff --git a/build/Android.executable.mk b/build/Android.executable.mk
index 7619f74..d00002a 100644
--- a/build/Android.executable.mk
+++ b/build/Android.executable.mk
@@ -91,20 +91,24 @@
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),target,ndebug))
   $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),target,ndebug))
   $(eval $(call build-art-executable,oatexec,$(OATEXEC_SRC_FILES),target,ndebug))
+  $(eval $(call build-art-executable,oatopt,$(OATOPT_SRC_FILES),target,ndebug))
 endif
 ifeq ($(ART_BUILD_TARGET_DEBUG),true)
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),target,debug))
   $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),target,debug))
   $(eval $(call build-art-executable,oatexec,$(OATEXEC_SRC_FILES),target,debug))
+  $(eval $(call build-art-executable,oatopt,$(OATOPT_SRC_FILES),target,debug))
 endif
 ifeq ($(ART_BUILD_HOST_NDEBUG),true)
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),host,ndebug))
   $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),host,ndebug))
   $(eval $(call build-art-executable,oatexec,$(OATEXEC_SRC_FILES),host,ndebug))
+  $(eval $(call build-art-executable,oatopt,$(OATOPT_SRC_FILES),host,ndebug))
 endif
 ifeq ($(ART_BUILD_HOST_DEBUG),true)
   $(eval $(call build-art-executable,dex2oat,$(DEX2OAT_SRC_FILES),host,debug))
   $(eval $(call build-art-executable,oatdump,$(OATDUMP_SRC_FILES),host,debug))
   $(eval $(call build-art-executable,oatexec,$(OATEXEC_SRC_FILES),host,debug))
+  $(eval $(call build-art-executable,oatopt,$(OATOPT_SRC_FILES),host,debug))
 endif
 
diff --git a/build/Android.oat.mk b/build/Android.oat.mk
index 59bb771..846cda3 100644
--- a/build/Android.oat.mk
+++ b/build/Android.oat.mk
@@ -20,8 +20,8 @@
 DEX2OAT := $(DEX2OATD)
 
 # TODO: change DEX2OAT_DEPENDENCY to order-only prerequisite when output is stable
+# DEX2OAT_DEPENDENCY := | $(DEX2OAT)  # only build dex2oat if needed to build oat files
 DEX2OAT_DEPENDENCY := $(DEX2OAT)    # when dex2oat changes, rebuild all oat files
-DEX2OAT_DEPENDENCY := | $(DEX2OAT)  # only build dex2oat if needed to build oat files
 
 OATDUMP := $(HOST_OUT_EXECUTABLES)/oatdump$(HOST_EXECUTABLE_SUFFIX)
 OATDUMPD := $(HOST_OUT_EXECUTABLES)/oatdumpd$(HOST_EXECUTABLE_SUFFIX)
diff --git a/src/class_linker.cc b/src/class_linker.cc
index f610f31..9fd8f0b 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -573,16 +573,8 @@
 
 const OatFile* ClassLinker::FindOatFile(const DexFile& dex_file) {
   MutexLock mu(lock_);
-  std::string dex_file_location = dex_file.GetLocation();
-  std::string location(dex_file_location);
-  CHECK(StringPiece(location).ends_with(".dex")
-        || StringPiece(location).ends_with(".zip")
-        || StringPiece(location).ends_with(".jar")
-        || StringPiece(location).ends_with(".apk"));
-  location.erase(location.size()-3);
-  location += "oat";
   // TODO: check if dex_file matches an OatDexFile location and checksum
-  return FindOatFile(location);
+  return FindOatFile(OatFile::DexFileToOatFilename(dex_file));
 }
 
 const OatFile* ClassLinker::FindOatFile(const std::string& location) {
@@ -601,10 +593,7 @@
       return NULL;
     }
     // not found in /foo/bar/baz.oat? try /data/art-cache/foo@bar@baz.oat
-    std::string art_cache = GetArtCacheOrDie();
-    std::string cache_file(location, 1); // skip leading slash
-    std::replace(cache_file.begin(), cache_file.end(), '/', '@');
-    std::string cache_location = art_cache + "/" + cache_file;
+    std::string cache_location = GetArtCacheOatFilenameOrDie(location);
     oat_file = OatFile::Open(cache_location, "", NULL);
     if (oat_file  == NULL) {
       LOG(ERROR) << "Failed to open oat file from " << location << " or " << cache_location << ".";
diff --git a/src/dex2oat.cc b/src/dex2oat.cc
index e8cd26d..a4d7449 100644
--- a/src/dex2oat.cc
+++ b/src/dex2oat.cc
@@ -86,6 +86,9 @@
 
   for (int i = 0; i < argc; i++) {
     const StringPiece option(argv[i]);
+    if (false) {
+      LOG(INFO) << "dex2oat: option[" << i << "]=" << argv[i];
+    }
     if (option.starts_with("--dex-file=")) {
       dex_filenames.push_back(option.substr(strlen("--dex-file=")).data());
     } else if (option.starts_with("--method=")) {
@@ -142,7 +145,7 @@
   }
 
   Runtime::Options options;
- std::string boot_class_path_string;
+  std::string boot_class_path_string;
   if (boot_image_option.empty()) {
     boot_class_path_string += "-Xbootclasspath:";
     for (size_t i = 0; i < dex_filenames.size()-1; i++) {
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 235c734..a11744d 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -107,7 +107,7 @@
   return OpenMemory(dex_file, length, location.ToString(), map.release());
 }
 
-static const char* kClassesDex = "classes.dex";
+const char* DexFile::kClassesDex = "classes.dex";
 
 class LockedFd {
  public:
@@ -643,7 +643,7 @@
   ParameterIterator* it = GetParameterIterator(GetProtoId(method->GetProtoIdx()));
   for (uint32_t i = 0; i < parameters_size && it->HasNext(); ++i, it->Next()) {
     if (arg_reg >= code_item->registers_size_) {
-      LOG(FATAL) << "invalid stream";
+      LOG(ERROR) << "invalid stream";
       return;
     }
     int32_t id = DecodeUnsignedLeb128P1(&stream);
@@ -669,7 +669,7 @@
   }
 
   if (it->HasNext()) {
-    LOG(FATAL) << "invalid stream";
+    LOG(ERROR) << "invalid stream";
     return;
   }
 
@@ -695,7 +695,7 @@
       case DBG_START_LOCAL_EXTENDED:
         reg = DecodeUnsignedLeb128(&stream);
         if (reg > code_item->registers_size_) {
-          LOG(FATAL) << "invalid stream";
+          LOG(ERROR) << "invalid stream";
           return;
         }
 
@@ -718,7 +718,7 @@
       case DBG_END_LOCAL:
         reg = DecodeUnsignedLeb128(&stream);
         if (reg > code_item->registers_size_) {
-          LOG(FATAL) << "invalid stream";
+          LOG(ERROR) << "invalid stream";
           return;
         }
 
@@ -731,13 +731,13 @@
       case DBG_RESTART_LOCAL:
         reg = DecodeUnsignedLeb128(&stream);
         if (reg > code_item->registers_size_) {
-          LOG(FATAL) << "invalid stream";
+          LOG(ERROR) << "invalid stream";
           return;
         }
 
         if (need_locals) {
           if (local_in_reg[reg].name_ == NULL || local_in_reg[reg].descriptor_ == NULL) {
-            LOG(FATAL) << "invalid stream";
+            LOG(ERROR) << "invalid stream";
             return;
           }
 
diff --git a/src/dex_file.h b/src/dex_file.h
index 77189f8..7d9e6f9 100644
--- a/src/dex_file.h
+++ b/src/dex_file.h
@@ -31,6 +31,9 @@
   static const byte kDexMagicVersion[];
   static const size_t kSha1DigestSize = 20;
 
+  // name of the DexFile entry within a zip archive
+  static const char* kClassesDex;
+
   static const byte kEncodedValueTypeMask = 0x1f;  // 0b11111
   static const byte kEncodedValueArgShift = 5;
 
diff --git a/src/oat_file.cc b/src/oat_file.cc
index 8a4aa9a..23eae7c 100644
--- a/src/oat_file.cc
+++ b/src/oat_file.cc
@@ -10,6 +10,17 @@
 
 namespace art {
 
+std::string OatFile::DexFileToOatFilename(const DexFile& dex_file) {
+  std::string location(dex_file.GetLocation());
+  CHECK(StringPiece(location).ends_with(".dex")
+        || StringPiece(location).ends_with(".zip")
+        || StringPiece(location).ends_with(".jar")
+        || StringPiece(location).ends_with(".apk"));
+  location.erase(location.size()-3);
+  location += "oat";
+  return location;
+}
+
 OatFile* OatFile::Open(const std::string& filename,
                        const std::string& strip_location_prefix,
                        byte* requested_base) {
diff --git a/src/oat_file.h b/src/oat_file.h
index 9f95b3b..1d3367c 100644
--- a/src/oat_file.h
+++ b/src/oat_file.h
@@ -16,6 +16,9 @@
 class OatFile {
  public:
 
+  // Returns an OatFile name based on a DexFile location
+  static std::string DexFileToOatFilename(const DexFile& dex_file);
+
   // Open an oat file. Returns NULL on failure.  Requested base can
   // optionally be used to request where the file should be loaded.
   static OatFile* Open(const std::string& filename,
diff --git a/src/oatdump.cc b/src/oatdump.cc
index 46f6c24..a5c8c7b 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -109,8 +109,9 @@
     os << "location: " << dex_file_location;
     if (!host_prefix.empty()) {
       dex_file_location = host_prefix + dex_file_location;
-      os << " (" << dex_file_location << ")\n";
+      os << " (" << dex_file_location << ")";
     }
+    os << "\n";
     os << StringPrintf("checksum: %08x\n", oat_dex_file.GetDexFileChecksum());
     const DexFile* dex_file = DexFile::Open(dex_file_location, "");
     if (dex_file == NULL) {
@@ -282,8 +283,9 @@
     os << oat_location;
     if (!host_prefix.empty()) {
       oat_location = host_prefix + oat_location;
-      os << " (" << oat_location << ")\n";
+      os << " (" << oat_location << ")";
     }
+    os << "\n";
     const OatFile* oat_file = class_linker->FindOatFile(oat_location);
     if (oat_file == NULL) {
       os << "NOT FOUND\n";
diff --git a/src/oatopt.cc b/src/oatopt.cc
new file mode 100644
index 0000000..367e5bf
--- /dev/null
+++ b/src/oatopt.cc
@@ -0,0 +1,144 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "dex_file.h"
+#include "file.h"
+#include "logging.h"
+#include "oat_file.h"
+#include "os.h"
+#include "UniquePtr.h"
+#include "zip_archive.h"
+
+namespace art {
+
+int ProcessZipFile(int zip_fd, int cache_fd, const char* zip_name, const char *flags) {
+  // TODO: need to read/write to installd opened file descriptors
+  if (false) {
+    UniquePtr<ZipArchive> zip_archive(ZipArchive::Open(zip_fd));
+    if (zip_archive.get() == NULL) {
+      LOG(ERROR) << "Failed to open " << zip_name << " when looking for classes.dex";
+      return -1;
+    }
+
+    UniquePtr<ZipEntry> zip_entry(zip_archive->Find(DexFile::kClassesDex));
+    if (zip_entry.get() == NULL) {
+      LOG(ERROR) << "Failed to find classes.dex within " << zip_name;
+      return -1;
+    }
+
+    UniquePtr<File> file(OS::FileFromFd("oatopt cache file descriptor", cache_fd));
+    bool success = zip_entry->Extract(*file);
+    if (!success) {
+      LOG(ERROR) << "Failed to extract classes.dex from " << zip_name;
+      return -1;
+    }
+  }
+
+  // Opening a zip file for a dex will extract to art-cache
+  UniquePtr<const DexFile> dex_file(DexFile::Open(zip_name, ""));
+  if (dex_file.get() == NULL) {
+    LOG(ERROR) << "Failed to open " << zip_name;
+    return -1;
+  }
+
+  std::string dex_file_option("--dex-file=");
+  dex_file_option += zip_name;
+
+  std::string oat_file_option("--oat=");
+  oat_file_option += GetArtCacheOatFilenameOrDie(OatFile::DexFileToOatFilename(*dex_file.get()));
+
+  execl("/system/bin/dex2oatd",
+        "/system/bin/dex2oatd",
+        "-Xms64m",
+        "-Xmx64m",
+        "--boot-image=/data/art-cache/boot.art",
+        dex_file_option.c_str(),
+        oat_file_option.c_str(),
+        NULL);
+  PLOG(FATAL) << "execl(dex2oatd) failed";
+  return -1;
+}
+
+// Parse arguments.  We want:
+//   0. (name of command -- ignored)
+//   1. "--zip"
+//   2. zip fd (input, read-only)
+//   3. cache fd (output, read-write, locked with flock)
+//   4. filename of zipfile
+//   5. flags
+int FromZip(const int argc, const char* const argv[]) {
+  if (argc != 6) {
+    LOG(ERROR) << "Wrong number of args for --zip (found " << argc << ")";
+    return -1;
+  }
+
+  // ignore program name
+
+  // verify --zip
+  CHECK_STREQ(argv[1], "--zip");
+
+  char* zip_end;
+  int zip_fd = strtol(argv[2], &zip_end, 0);
+  if (*zip_end != '\0') {
+    LOG(ERROR) << "bad zip fd: " << argv[2];
+    return -1;
+  }
+#ifndef NDEBUG
+  LOG(INFO) << "zip_fd=" << zip_fd;
+#endif
+
+  char* cache_end;
+  int cache_fd = strtol(argv[3], &cache_end, 0);
+  if (*cache_end != '\0') {
+    LOG(ERROR) << "bad cache fd: " << argv[3];
+    return -1;
+  }
+#ifndef NDEBUG
+  LOG(INFO) << "cache_fd=" << cache_fd;
+#endif
+
+  const char* zip_name = argv[4];
+#ifndef NDEBUG
+  LOG(INFO) << "zip_name=" << zip_name;
+#endif
+
+  const char* flags = argv[5];
+#ifndef NDEBUG
+  LOG(INFO) << "flags=" << flags;
+#endif
+
+  return ProcessZipFile(zip_fd, cache_fd, zip_name, flags);
+}
+
+int oatopt(int argc, char** argv) {
+
+  setvbuf(stdout, NULL, _IONBF, 0);
+
+  if (true) {
+    for (int i = 0; i < argc; ++i) {
+      LOG(INFO) << "oatopt: option[" << i << "]=" << argv[i];
+    }
+  }
+
+  if (argc > 1) {
+    if (strcmp(argv[1], "--zip") == 0) {
+      return FromZip(argc, argv);
+    }
+  }
+
+  fprintf(stderr,
+          "Usage:\n\n"
+          "Short version: Don't use this.\n\n"
+          "Slightly longer version: This system-internal tool is used to extract\n"
+          "dex files and produce oat files. See the source code for details.\n");
+
+  return 1;
+}
+
+} // namespace art
+
+int main(int argc, char** argv) {
+  return art::oatopt(argc, argv);
+}
diff --git a/src/utils.cc b/src/utils.cc
index bc51c2a..4f4cd8f 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -514,6 +514,14 @@
   return art_cache;
 }
 
+std::string GetArtCacheOatFilenameOrDie(const std::string& location) {
+  std::string art_cache = GetArtCacheOrDie();
+  CHECK_EQ(location[0], '/');
+  std::string cache_file(location, 1); // skip leading slash
+  std::replace(cache_file.begin(), cache_file.end(), '/', '@');
+  return art_cache + "/" + cache_file;
+}
+
 }  // namespace art
 
 // Neither bionic nor glibc exposes gettid(2).
diff --git a/src/utils.h b/src/utils.h
index 7778918..35b350b 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -212,9 +212,12 @@
 // implementation-defined limit.
 void SetThreadName(const char* name);
 
-// Returns the art-cache location or dies trying
+// Returns the art-cache location, or dies trying.
 std::string GetArtCacheOrDie();
 
+// Returns the art-cache location for an OatFile, or dies trying.
+std::string GetArtCacheOatFilenameOrDie(const std::string& location);
+
 }  // namespace art
 
 #endif  // ART_SRC_UTILS_H_
diff --git a/src/zip_archive.cc b/src/zip_archive.cc
index 82d97ec..41a25de 100644
--- a/src/zip_archive.cc
+++ b/src/zip_archive.cc
@@ -257,7 +257,6 @@
   }
 }
 
-// return new ZipArchive instance on success, NULL on error.
 ZipArchive* ZipArchive::Open(const std::string& filename) {
   DCHECK(!filename.empty());
   int fd = open(filename.c_str(), O_RDONLY | O_CLOEXEC, 0);
@@ -265,6 +264,10 @@
     PLOG(WARNING) << "Unable to open '" << filename << "'";
     return NULL;
   }
+  return Open(fd);
+}
+
+ZipArchive* ZipArchive::Open(int fd) {
   UniquePtr<ZipArchive> zip_archive(new ZipArchive(fd));
   if (zip_archive.get() == NULL) {
       return NULL;
diff --git a/src/zip_archive.h b/src/zip_archive.h
index 0c9e6c2..13a22cc 100644
--- a/src/zip_archive.h
+++ b/src/zip_archive.h
@@ -102,7 +102,10 @@
   static const int32_t kCDECommentLen   = 32;  // offset to comment length
   static const int32_t kCDELocalOffset  = 42;  // offset to local hdr
 
+  // return new ZipArchive instance on success, NULL on error.
   static ZipArchive* Open(const std::string& filename);
+  static ZipArchive* Open(int fd);
+
   ZipEntry* Find(const char * name);
 
   ~ZipArchive() {