Merge "Fix debuggable compiler flag detection for secondary dex files" into mnc-dev
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index b4a45c6..3cf458a 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -979,7 +979,10 @@
       oss.str("");  // Reset.
       oss << kRuntimeISA;
       key_value_store_->Put(OatHeader::kDex2OatHostKey, oss.str());
-      key_value_store_->Put(OatHeader::kPicKey, compile_pic ? "true" : "false");
+      key_value_store_->Put(OatHeader::kPicKey,
+                            compile_pic ? OatHeader::kTrueValue : OatHeader::kFalseValue);
+      key_value_store_->Put(OatHeader::kDebuggableKey,
+                            debuggable ? OatHeader::kTrueValue : OatHeader::kFalseValue);
     }
   }
 
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index b099088..292f830 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -819,6 +819,34 @@
   }
 }
 
+const OatFile* ClassLinker::GetBootOatFile() {
+  // To grab the boot oat, look at the dex files in the boot classpath. Any of those is fine, as
+  // they were all compiled into the same oat file. So grab the first one, which is guaranteed to
+  // exist if the boot class-path isn't empty.
+  if (boot_class_path_.empty()) {
+    return nullptr;
+  }
+  const DexFile* boot_dex_file = boot_class_path_[0];
+  // Is it from an oat file?
+  if (boot_dex_file->GetOatDexFile() != nullptr) {
+    return boot_dex_file->GetOatDexFile()->GetOatFile();
+  }
+  return nullptr;
+}
+
+const OatFile* ClassLinker::GetPrimaryOatFile() {
+  ReaderMutexLock mu(Thread::Current(), dex_lock_);
+  const OatFile* boot_oat_file = GetBootOatFile();
+  if (boot_oat_file != nullptr) {
+    for (const OatFile* oat_file : oat_files_) {
+      if (oat_file != boot_oat_file) {
+        return oat_file;
+      }
+    }
+  }
+  return nullptr;
+}
+
 // Check for class-def collisions in dex files.
 //
 // This works by maintaining a heap with one class from each dex file, sorted by the class
@@ -835,18 +863,7 @@
 
   // Add dex files from already loaded oat files, but skip boot.
   {
-    // To grab the boot oat, look at the dex files in the boot classpath. Any of those is fine, as
-    // they were all compiled into the same oat file. So grab the first one, which is guaranteed to
-    // exist if the boot class-path isn't empty.
-    const OatFile* boot_oat = nullptr;
-    if (!boot_class_path_.empty()) {
-      const DexFile* boot_dex_file = boot_class_path_[0];
-      // Is it from an oat file?
-      if (boot_dex_file->GetOatDexFile() != nullptr) {
-        boot_oat = boot_dex_file->GetOatDexFile()->GetOatFile();
-      }
-    }
-
+    const OatFile* boot_oat = GetBootOatFile();
     for (const OatFile* loaded_oat_file : oat_files_) {
       if (loaded_oat_file == boot_oat) {
         continue;
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 57989b2..95c8aa0 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -295,6 +295,10 @@
     return boot_class_path_;
   }
 
+  // Returns the first non-image oat file in the class path.
+  const OatFile* GetPrimaryOatFile()
+      LOCKS_EXCLUDED(dex_lock_);
+
   void VisitClasses(ClassVisitor* visitor, void* arg)
       LOCKS_EXCLUDED(Locks::classlinker_classes_lock_)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
@@ -615,6 +619,9 @@
   const OatFile* FindOpenedOatFileFromOatLocation(const std::string& oat_location)
       LOCKS_EXCLUDED(dex_lock_);
 
+  // Returns the boot image oat file.
+  const OatFile* GetBootOatFile() SHARED_LOCKS_REQUIRED(dex_lock_);
+
   mirror::ArtMethod* CreateProxyConstructor(Thread* self, Handle<mirror::Class> klass,
                                             mirror::Class* proxy_class)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index f350038..99f5d45 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -203,6 +203,9 @@
   oat_file_option_string += ImageHeader::GetOatLocationFromImageLocation(image_filename);
   arg_vector.push_back(oat_file_option_string);
 
+  // Note: we do not generate a fully debuggable boot image so we do not pass the
+  // compiler flag --debuggable here.
+
   Runtime::Current()->AddCurrentRuntimeFeaturesAsDex2OatArguments(&arg_vector);
   CHECK_EQ(image_isa, kRuntimeISA)
       << "We should always be generating an image for the current isa.";
diff --git a/runtime/oat.cc b/runtime/oat.cc
index c223e2e..4f6aabc 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -27,6 +27,8 @@
 
 constexpr uint8_t OatHeader::kOatMagic[4];
 constexpr uint8_t OatHeader::kOatVersion[4];
+constexpr const char OatHeader::kTrueValue[];
+constexpr const char OatHeader::kFalseValue[];
 
 static size_t ComputeOatHeaderSize(const SafeMap<std::string, std::string>* variable_data) {
   size_t estimate = 0U;
@@ -443,9 +445,16 @@
 }
 
 bool OatHeader::IsPic() const {
-  const char* pic_string = GetStoreValueByKey(OatHeader::kPicKey);
-  static const char kTrue[] = "true";
-  return (pic_string != nullptr && strncmp(pic_string, kTrue, sizeof(kTrue)) == 0);
+  return IsKeyEnabled(OatHeader::kPicKey);
+}
+
+bool OatHeader::IsDebuggable() const {
+  return IsKeyEnabled(OatHeader::kDebuggableKey);
+}
+
+bool OatHeader::IsKeyEnabled(const char* key) const {
+  const char* key_value = GetStoreValueByKey(key);
+  return (key_value != nullptr && strncmp(key_value, kTrueValue, sizeof(kTrueValue)) == 0);
 }
 
 void OatHeader::Flatten(const SafeMap<std::string, std::string>* key_value_store) {
diff --git a/runtime/oat.h b/runtime/oat.h
index aaf442a..604e161 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -32,14 +32,18 @@
 class PACKED(4) OatHeader {
  public:
   static constexpr uint8_t kOatMagic[] = { 'o', 'a', 't', '\n' };
-  static constexpr uint8_t kOatVersion[] = { '0', '6', '2', '\0' };
+  static constexpr uint8_t kOatVersion[] = { '0', '6', '3', '\0' };
 
   static constexpr const char* kImageLocationKey = "image-location";
   static constexpr const char* kDex2OatCmdLineKey = "dex2oat-cmdline";
   static constexpr const char* kDex2OatHostKey = "dex2oat-host";
   static constexpr const char* kPicKey = "pic";
+  static constexpr const char* kDebuggableKey = "debuggable";
   static constexpr const char* kClassPathKey = "classpath";
 
+  static constexpr const char kTrueValue[] = "true";
+  static constexpr const char kFalseValue[] = "false";
+
   static OatHeader* Create(InstructionSet instruction_set,
                            const InstructionSetFeatures* instruction_set_features,
                            const std::vector<const DexFile*>* dex_files,
@@ -99,6 +103,7 @@
 
   size_t GetHeaderSize() const;
   bool IsPic() const;
+  bool IsDebuggable() const;
 
  private:
   OatHeader(InstructionSet instruction_set,
@@ -108,6 +113,9 @@
             uint32_t image_file_location_oat_data_begin,
             const SafeMap<std::string, std::string>* variable_data);
 
+  // Returns true if the value of the given key is "true", false otherwise.
+  bool IsKeyEnabled(const char* key) const;
+
   void Flatten(const SafeMap<std::string, std::string>* variable_data);
 
   uint8_t magic_[4];
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index b0cbd0e..63ee4b1 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -703,6 +703,10 @@
   // TODO: Check against oat_patches. b/18144996
 }
 
+bool OatFile::IsDebuggable() const {
+  return GetOatHeader().IsDebuggable();
+}
+
 static constexpr char kDexClassPathEncodingSeparator = '*';
 
 std::string OatFile::EncodeDexFileDependencies(const std::vector<const DexFile*>& dex_files) {
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index b32dd22..12e9f6c 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -81,6 +81,9 @@
 
   bool IsPic() const;
 
+  // Indicates whether the oat file was compiled with full debugging capability.
+  bool IsDebuggable() const;
+
   ElfFile* GetElfFile() const {
     CHECK_NE(reinterpret_cast<uintptr_t>(elf_file_.get()), reinterpret_cast<uintptr_t>(nullptr))
         << "Cannot get an elf file from " << GetLocation();
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 84d9505..094d8b7 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -690,12 +690,20 @@
     return false;
   }
 
+  ClassLinker* linker = runtime->GetClassLinker();
+  CHECK(linker != nullptr) << "ClassLinker is not created yet";
+  const OatFile* primary_oat_file = linker->GetPrimaryOatFile();
+  const bool debuggable = primary_oat_file != nullptr && primary_oat_file->IsDebuggable();
+
   std::vector<std::string> argv;
   argv.push_back(runtime->GetCompilerExecutable());
   argv.push_back("--runtime-arg");
   argv.push_back("-classpath");
   argv.push_back("--runtime-arg");
   argv.push_back(runtime->GetClassPathString());
+  if (debuggable) {
+    argv.push_back("--debuggable");
+  }
   runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
 
   if (!runtime->IsVerificationEnabled()) {
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 2633898..2618661 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1678,10 +1678,6 @@
   std::string feature_string("--instruction-set-features=");
   feature_string += features->GetFeatureString();
   argv->push_back(feature_string);
-
-  if (Dbg::IsJdwpConfigured()) {
-    argv->push_back("--debuggable");
-  }
 }
 
 void Runtime::UpdateProfilerState(int state) {