Fix dexlayout fixed point test

Previously, the dex location did not match the one in the profile.
This caused the second dexlayout to not match the checksum and not
use the profile.

Also added a generic function to generate a profile based on an input
dex.

Test: test-art-host

Bug: 62040831

Change-Id: I2b4fb383ec7a46b158f763de13ecbcd8a8c6180d
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 0a465c4..bcf48fd 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -40,6 +40,7 @@
   Interfaces \
   Lookup \
   Main \
+  ManyMethods \
   MethodTypes \
   MultiDex \
   MultiDexModifiedSecondary \
@@ -103,6 +104,7 @@
 ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
 ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
 ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex
+ART_GTEST_dexlayout_test_DEX_DEPS := ManyMethods
 ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
 ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
 ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index a0533f2..1d09a7f 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -23,7 +23,9 @@
 
 #include "base/unix_file/fd_file.h"
 #include "common_runtime_test.h"
+#include "dex_file-inl.h"
 #include "exec_utils.h"
+#include "jit/profile_compilation_info.h"
 #include "utils.h"
 
 namespace art {
@@ -40,9 +42,6 @@
     "qAAAAAYAAAACAAAAwAAAAAEgAAACAAAAAAEAAAIgAAAHAAAAMAEAAAMgAAACAAAAaQEAAAAgAAAC"
     "AAAAdQEAAAAQAAABAAAAjAEAAA==";
 
-static const char kDexFileLayoutInputProfile[] =
-    "cHJvADAwNwAAAAAAAAgAAAB4AQMAAAAAAQ==";
-
 // Dex file with catch handler unreferenced by try blocks.
 // Constructed by building a dex file with try/catch blocks and hex editing.
 static const char kUnreferencedCatchHandlerInputDex[] =
@@ -317,6 +316,56 @@
     return true;
   }
 
+  // Create a profile with some subset of methods and classes.
+  void CreateProfile(const std::string& input_dex,
+                     const std::string& out_profile,
+                     const std::string& dex_location) {
+    std::vector<std::unique_ptr<const DexFile>> dex_files;
+    std::string error_msg;
+    bool result = DexFile::Open(input_dex.c_str(),
+                                input_dex,
+                                false,
+                                &error_msg,
+                                &dex_files);
+
+    ASSERT_TRUE(result) << error_msg;
+    ASSERT_GE(dex_files.size(), 1u);
+
+    size_t profile_methods = 0;
+    size_t profile_classes = 0;
+    ProfileCompilationInfo pfi;
+    std::vector<ProfileMethodInfo> pmis;
+    std::set<DexCacheResolvedClasses> classes;
+    for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
+      for (uint32_t i = 0; i < dex_file->NumMethodIds(); i += 2) {
+        if ((i & 3) != 0) {
+          pfi.AddMethodIndex(dex_location,
+                             dex_file->GetLocationChecksum(),
+                             i);
+          ++profile_methods;
+        }
+      }
+      DexCacheResolvedClasses cur_classes(dex_location,
+                                          dex_location,
+                                          dex_file->GetLocationChecksum());
+      // Add every even class too.
+      for (uint32_t i = 0; i < dex_file->NumClassDefs(); i += 1) {
+        cur_classes.AddClass(dex_file->GetClassDef(i).class_idx_);
+        ++profile_classes;
+      }
+    }
+    pfi.AddMethodsAndClasses(pmis, classes);
+    // Write to provided file.
+    std::unique_ptr<File> file(OS::CreateEmptyFile(out_profile.c_str()));
+    ASSERT_TRUE(file != nullptr);
+    pfi.Save(file->Fd());
+    if (file->FlushCloseOrErase() != 0) {
+      PLOG(FATAL) << "Could not flush and close test file.";
+    }
+    EXPECT_GE(profile_methods, 0u);
+    EXPECT_GE(profile_classes, 0u);
+  }
+
   // Runs DexFileLayout test.
   bool DexFileLayoutExec(std::string* error_msg) {
     ScratchFile tmp_file;
@@ -328,7 +377,8 @@
     std::string dex_file = tmp_dir + "classes.dex";
     WriteFileBase64(kDexFileLayoutInputDex, dex_file.c_str());
     std::string profile_file = tmp_dir + "primary.prof";
-    WriteFileBase64(kDexFileLayoutInputProfile, profile_file.c_str());
+    CreateProfile(dex_file, profile_file, dex_file);
+    // WriteFileBase64(kDexFileLayoutInputProfile, profile_file.c_str());
     std::string output_dex = tmp_dir + "classes.dex.new";
 
     std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
@@ -358,11 +408,24 @@
     size_t tmp_last_slash = tmp_name.rfind("/");
     std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
 
-    // Write inputs and expected outputs.
+    // Unzip the test dex file to the classes.dex destination. It is required to unzip since
+    // opening from jar recalculates the dex location checksum.
     std::string dex_file = tmp_dir + "classes.dex";
-    WriteFileBase64(kDexFileLayoutInputDex, dex_file.c_str());
+
+    std::vector<std::string> unzip_args = {
+        "/usr/bin/unzip",
+        GetTestDexFileName("ManyMethods"),
+        "classes.dex",
+        "-d",
+        tmp_dir,
+    };
+    if (!art::Exec(unzip_args, error_msg)) {
+      LOG(ERROR) << "Failed to unzip dex";
+      return false;
+    }
+
     std::string profile_file = tmp_dir + "primary.prof";
-    WriteFileBase64(kDexFileLayoutInputProfile, profile_file.c_str());
+    CreateProfile(dex_file, profile_file, dex_file);
     std::string output_dex = tmp_dir + "classes.dex.new";
     std::string second_output_dex = tmp_dir + "classes.dex.new.new";
 
@@ -371,14 +434,19 @@
 
     // -v makes sure that the layout did not corrupt the dex file.
     std::vector<std::string> dexlayout_exec_argv =
-        { dexlayout, "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file };
+        { dexlayout, "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file };
     if (!::art::Exec(dexlayout_exec_argv, error_msg)) {
       return false;
     }
 
+    // Recreate the profile with the new dex location. This is required so that the profile dex
+    // location matches.
+    CreateProfile(dex_file, profile_file, output_dex);
+
     // -v makes sure that the layout did not corrupt the dex file.
+    // -i since the checksum won't match from the first layout.
     std::vector<std::string> second_dexlayout_exec_argv =
-        { dexlayout, "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, output_dex };
+        { dexlayout, "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, output_dex };
     if (!::art::Exec(second_dexlayout_exec_argv, error_msg)) {
       return false;
     }
@@ -436,13 +504,11 @@
   bool DexLayoutExec(ScratchFile* dex_file,
                      const char* dex_filename,
                      ScratchFile* profile_file,
-                     const char* profile_filename,
                      std::vector<std::string>& dexlayout_exec_argv) {
     WriteBase64ToFile(dex_filename, dex_file->GetFile());
     EXPECT_EQ(dex_file->GetFile()->Flush(), 0);
     if (profile_file != nullptr) {
-      WriteBase64ToFile(profile_filename, profile_file->GetFile());
-      EXPECT_EQ(profile_file->GetFile()->Flush(), 0);
+      CreateProfile(dex_file->GetFilename(), profile_file->GetFilename(), dex_file->GetFilename());
     }
     std::string error_msg;
     const bool result = ::art::Exec(dexlayout_exec_argv, &error_msg);
@@ -516,7 +582,6 @@
   ASSERT_TRUE(DexLayoutExec(&temp_dex,
                             kDexFileDuplicateOffset,
                             nullptr /* profile_file */,
-                            nullptr /* profile_filename */,
                             dexlayout_exec_argv));
 }
 
@@ -529,7 +594,6 @@
   ASSERT_TRUE(DexLayoutExec(&temp_dex,
                             kNullSetRefListElementInputDex,
                             nullptr /* profile_file */,
-                            nullptr /* profile_filename */,
                             dexlayout_exec_argv));
 }
 
@@ -543,7 +607,6 @@
   ASSERT_TRUE(DexLayoutExec(&temp_dex,
                             kMultiClassDataInputDex,
                             &temp_profile,
-                            kDexFileLayoutInputProfile,
                             dexlayout_exec_argv));
 }
 
@@ -557,7 +620,6 @@
   ASSERT_TRUE(DexLayoutExec(&temp_dex,
                             kUnalignedCodeInfoInputDex,
                             &temp_profile,
-                            kDexFileLayoutInputProfile,
                             dexlayout_exec_argv));
 }
 
@@ -571,7 +633,6 @@
   ASSERT_TRUE(DexLayoutExec(&temp_dex,
                             kClassDataBeforeCodeInputDex,
                             &temp_profile,
-                            kDexFileLayoutInputProfile,
                             dexlayout_exec_argv));
 }
 
@@ -584,7 +645,6 @@
   ASSERT_TRUE(DexLayoutExec(&temp_dex,
                             kUnknownTypeDebugInfoInputDex,
                             nullptr /* profile_file */,
-                            nullptr /* profile_filename */,
                             dexlayout_exec_argv));
 }
 
@@ -597,7 +657,6 @@
   ASSERT_TRUE(DexLayoutExec(&temp_dex,
                             kDuplicateCodeItemInputDex,
                             nullptr /* profile_file */,
-                            nullptr /* profile_filename */,
                             dexlayout_exec_argv));
 }
 
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index a29cc6c..24dbd05 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -98,9 +98,12 @@
   // Returns bin directory which contains host's prebuild tools.
   static std::string GetAndroidHostToolsDir();
 
-  // Returns bin directory wahich contains target's prebuild tools.
+  // Returns bin directory which contains target's prebuild tools.
   static std::string GetAndroidTargetToolsDir(InstructionSet isa);
 
+  // Retuerns the filename for a test dex (i.e. XandY or ManyMethods).
+  std::string GetTestDexFileName(const char* name) const;
+
  protected:
   // Allow subclases such as CommonCompilerTest to add extra options.
   virtual void SetUpRuntimeOptions(RuntimeOptions* options ATTRIBUTE_UNUSED) {}
@@ -127,8 +130,6 @@
 
   std::string GetTestAndroidRoot();
 
-  std::string GetTestDexFileName(const char* name) const;
-
   std::vector<std::unique_ptr<const DexFile>> OpenTestDexFiles(const char* name);
 
   std::unique_ptr<const DexFile> OpenTestDexFile(const char* name)
diff --git a/runtime/jit/profile_compilation_info.h b/runtime/jit/profile_compilation_info.h
index e903e2d..a9a5b13 100644
--- a/runtime/jit/profile_compilation_info.h
+++ b/runtime/jit/profile_compilation_info.h
@@ -276,6 +276,9 @@
 
   ArenaAllocator* GetArena() { return &arena_; }
 
+  // Add a method index to the profile (without inline caches).
+  bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
+
  private:
   enum ProfileLoadSatus {
     kProfileLoadWouldOverwiteData,
@@ -333,9 +336,6 @@
   // already exists but has a different checksum
   DexFileData* GetOrAddDexFileData(const std::string& profile_key, uint32_t checksum);
 
-  // Add a method index to the profile (without inline caches).
-  bool AddMethodIndex(const std::string& dex_location, uint32_t checksum, uint16_t method_idx);
-
   // Add a method to the profile using its online representation (containing runtime structures).
   bool AddMethod(const ProfileMethodInfo& pmi);
 
diff --git a/test/ManyMethods/ManyMethods.java b/test/ManyMethods/ManyMethods.java
new file mode 100644
index 0000000..b3a57f6
--- /dev/null
+++ b/test/ManyMethods/ManyMethods.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+class ManyMethods {
+  static class Strings {
+    public static String msg0 = "Hello World";
+    public static String msg1 = "Hello World1";
+    public static String msg2 = "Hello World2";
+    public static String msg3 = "Hello World3";
+    public static String msg4 = "Hello World4";
+    public static String msg5 = "Hello World5";
+    public static String msg6 = "Hello World6";
+    public static String msg7 = "Hello World7";
+    public static String msg8 = "Hello World8";
+    public static String msg9 = "Hello World9";
+  }
+
+  static class Printer {
+    static void Print(String s) {
+      System.out.println(s);
+    }
+  }
+
+  static class Printer2 {
+    static void Print(String s) {
+      System.out.println("AAA" + s);
+    }
+  }
+
+  public static void Print0() {
+    Printer.Print(Strings.msg0);
+  }
+
+  public static void Print1() {
+    Printer.Print(Strings.msg1);
+  }
+
+  public static void Print2() {
+    Printer.Print(Strings.msg2);
+  }
+
+  public static void Print3() {
+    Printer.Print(Strings.msg1);
+  }
+
+  public static void Print4() {
+    Printer.Print(Strings.msg2);
+  }
+
+  public static void Print5() {
+    Printer.Print(Strings.msg3);
+  }
+
+  public static void Print6() {
+    Printer2.Print(Strings.msg4);
+  }
+
+  public static void Print7() {
+    Printer.Print(Strings.msg5);
+  }
+
+  public static void Print8() {
+    Printer.Print(Strings.msg6);
+  }
+
+  public static void Print9() {
+    Printer2.Print(Strings.msg7);
+  }
+
+  public static void Print10() {
+    Printer2.Print(Strings.msg8);
+  }
+
+  public static void Print11() {
+    Printer.Print(Strings.msg9);
+  }
+
+  public static void main(String args[]) {
+    Print0();
+    Print1();
+    Print2();
+    Print3();
+    Print4();
+    Print5();
+    Print6();
+    Print7();
+    Print8();
+    Print9();
+    Print10();
+    Print11();
+  }
+}