Merge "ART: Enhance comment"
diff --git a/runtime/class_loader_context.cc b/runtime/class_loader_context.cc
index e7051b3..e97c6a0 100644
--- a/runtime/class_loader_context.cc
+++ b/runtime/class_loader_context.cc
@@ -16,6 +16,9 @@
 
 #include "class_loader_context.h"
 
+#include <stdlib.h>
+
+#include "android-base/file.h"
 #include "art_field-inl.h"
 #include "base/dchecked_vector.h"
 #include "base/stl_util.h"
@@ -202,10 +205,21 @@
   for (ClassLoaderInfo& info : class_loader_chain_) {
     for (const std::string& cp_elem : info.classpath) {
       // If path is relative, append it to the provided base directory.
-      std::string location = cp_elem;
-      if (location[0] != '/') {
-        location = classpath_dir + '/' + location;
+      std::string raw_location = cp_elem;
+      if (raw_location[0] != '/') {
+        raw_location = classpath_dir + '/' + raw_location;
       }
+
+      std::string location;  // the real location of the class path element.
+
+      if (!android::base::Realpath(raw_location, &location)) {
+        // If we can't get the realpath of the location there might be something wrong with the
+        // classpath (maybe the file was deleted).
+        // Do not continue in this case and return false.
+        PLOG(ERROR) << "Could not get the realpath of dex location " << raw_location;
+        return false;
+      }
+
       std::string error_msg;
       // When opening the dex files from the context we expect their checksum to match their
       // contents. So pass true to verify_checksum.
diff --git a/runtime/class_loader_context_test.cc b/runtime/class_loader_context_test.cc
index d4688c1..458f9f3 100644
--- a/runtime/class_loader_context_test.cc
+++ b/runtime/class_loader_context_test.cc
@@ -15,14 +15,16 @@
  */
 
 #include <gtest/gtest.h>
+#include <stdlib.h>
 
-#include "class_loader_context.h"
-#include "common_runtime_test.h"
 
 #include "base/dchecked_vector.h"
 #include "base/stl_util.h"
+#include "class_loader_context.h"
 #include "class_linker.h"
+#include "common_runtime_test.h"
 #include "dex_file.h"
+#include "dex2oat_environment_test.h"
 #include "handle_scope-inl.h"
 #include "mirror/class.h"
 #include "mirror/class_loader.h"
@@ -89,9 +91,22 @@
             info.opened_dex_files[cur_open_dex_index++];
         std::unique_ptr<const DexFile>& expected_dex_file = dex_files_for_cp_elem[i];
 
-        ASSERT_EQ(expected_dex_file->GetLocation(), opened_dex_file->GetLocation());
+        std::string expected_location = expected_dex_file->GetBaseLocation();
+        UniqueCPtr<const char[]> expected_real_location(
+            realpath(expected_location.c_str(), nullptr));
+        ASSERT_TRUE(expected_real_location != nullptr) << expected_location;
+        expected_location.assign(expected_real_location.get());
+        expected_location += DexFile::GetMultiDexSuffix(expected_dex_file->GetLocation());
+
+        ASSERT_EQ(expected_location, opened_dex_file->GetLocation());
         ASSERT_EQ(expected_dex_file->GetLocationChecksum(), opened_dex_file->GetLocationChecksum());
-        ASSERT_EQ(info.classpath[k], opened_dex_file->GetBaseLocation());
+
+        std::string class_path_location = info.classpath[k];
+        UniqueCPtr<const char[]> class_path_location_real(
+            realpath(class_path_location.c_str(), nullptr));
+        ASSERT_TRUE(class_path_location_real != nullptr);
+        class_path_location.assign(class_path_location_real.get());
+        ASSERT_EQ(class_path_location, opened_dex_file->GetBaseLocation());
       }
     }
   }
@@ -234,7 +249,6 @@
   std::string dex_name = GetTestDexFileName("Main");
   std::vector<std::unique_ptr<const DexFile>> dex_files = OpenTestDexFiles("Main");
 
-
   std::unique_ptr<ClassLoaderContext> context =
       ClassLoaderContext::Create(
           "PCL[" + multidex_name + ":" + myclass_dex_name + "];" +
@@ -253,6 +267,43 @@
   VerifyOpenDexFiles(context.get(), 1, all_dex_files1);
 }
 
+class ScratchSymLink {
+ public:
+  explicit ScratchSymLink(const std::string& file) {
+    // Use a temporary scratch file to get a unique name for the link.
+    ScratchFile scratchFile;
+    scratch_link_name_ = scratchFile.GetFilename() + ".link.jar";
+    CHECK_EQ(0, symlink(file.c_str(), scratch_link_name_.c_str()));
+  }
+
+  ~ScratchSymLink() {
+    CHECK_EQ(0, unlink(scratch_link_name_.c_str()));
+  }
+
+  const std::string& GetFilename() { return scratch_link_name_; }
+
+ private:
+  std::string scratch_link_name_;
+};
+
+TEST_F(ClassLoaderContextTest, OpenValidDexFilesSymLink) {
+  std::string myclass_dex_name = GetTestDexFileName("MyClass");
+  // Now replace the dex location with a symlink.
+  ScratchSymLink link(myclass_dex_name);
+
+  std::unique_ptr<ClassLoaderContext> context =
+      ClassLoaderContext::Create("PCL[" + link.GetFilename() + "]");
+
+  ASSERT_TRUE(context->OpenDexFiles(InstructionSet::kArm, /*classpath_dir*/ ""));
+
+  VerifyContextSize(context.get(), 1);
+  std::vector<std::vector<std::unique_ptr<const DexFile>>*> all_dex_files0;
+  std::vector<std::unique_ptr<const DexFile>> myclass_dex_files = OpenTestDexFiles("MyClass");
+  all_dex_files0.push_back(&myclass_dex_files);
+
+  VerifyOpenDexFiles(context.get(), 0, all_dex_files0);
+}
+
 TEST_F(ClassLoaderContextTest, OpenInvalidDexFilesMix) {
   std::string dex_name = GetTestDexFileName("Main");
   std::unique_ptr<ClassLoaderContext> context =
diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py
index b1274c9..49444d4 100755
--- a/test/testrunner/run_build_test_target.py
+++ b/test/testrunner/run_build_test_target.py
@@ -100,6 +100,8 @@
   run_test_command += ['-b']
   run_test_command += ['--host']
   run_test_command += ['--verbose']
+  run_test_command += ['--dex2oat-jobs']
+  run_test_command += ['4']
 
   sys.stdout.write(str(run_test_command) + '\n')
   sys.stdout.flush()
diff --git a/tools/ahat/src/dominators/DominatorsComputation.java b/tools/ahat/src/dominators/DominatorsComputation.java
index 9a2a272..58b7b59 100644
--- a/tools/ahat/src/dominators/DominatorsComputation.java
+++ b/tools/ahat/src/dominators/DominatorsComputation.java
@@ -88,33 +88,17 @@
     // Invariant: srcS.id < this.id
     public NodeS srcS;
 
+    // The largest id of the nodes we have seen so far on a path from the root
+    // to this node. Used to keep track of which nodes we have already seen
+    // and avoid processing them again.
+    public long seenid;
+
     // The set of nodes X reachable by 'this' on a path of nodes from the
     // root with increasing ids (possibly excluding X) that this node does not
     // dominate (this.id > X.domid).
-    // We can use a List instead of a Set for this because we guarentee that
-    // we don't add the same node more than once to the list (see below).
+    // We can use a List instead of a Set for this because we guarentee based
+    // on seenid that we don't add the same node more than once to the list.
     public List<NodeS> undom = new ArrayList<NodeS>();
-
-    // The largest id of the node X for which we did X.undom.add(this).
-    // This is an optimization to avoid adding duplicate node entries to the
-    // undom set.
-    //
-    // The first time we see this node, we reach it through a path of nodes
-    // with IDs 0,...,a,this. These form our src chain to the root.
-    //
-    // The next time we see this node, we reach it through a path of nodes
-    // with IDS 0,...,b,c,...,d,this. Where all 0,...,b < a and all c,...,d > a.
-    //
-    // The next time we see this node, we reach it through a path of nodes
-    // with IDS 0,...,e,f,...,g,this. With all 0,...,e < d and all f,...,g > d.
-    // And so on.
-    //
-    // The first time we see this node, we set undomid to a.id. Nodes 0,...,a
-    // will be added as undom in the 'revisit' phase of the node.
-    //
-    // The next times we see this node, we mark a+,...,d as undom and
-    // change undomid to d. And so on.
-    public long undomid;
   }
 
   private static class Link {
@@ -171,7 +155,7 @@
         dstS.domid = link.srcS.id;
         dstS.domS = link.srcS;
         dstS.srcS = link.srcS;
-        dstS.undomid = dstS.domid;
+        dstS.seenid = dstS.domid;
         nodes.add(dstS);
         link.dst.setDominatorsComputationState(dstS);
 
@@ -184,13 +168,11 @@
         NodeS srcS = link.srcS;
         boolean revisiting = dstS.domid < dstS.domS.id;
 
-        while (srcS.id > dstS.domid) {
-          if (srcS.id > dstS.undomid) {
-            srcS.undom.add(dstS);
-          }
+        while (srcS.id > dstS.seenid) {
+          srcS.undom.add(dstS);
           srcS = srcS.srcS;
         }
-        dstS.undomid = link.srcS.id;
+        dstS.seenid = link.srcS.id;
 
         if (srcS.id < dstS.domid) {
           // In this case, dstS.domid must be wrong, because we just found a