Optionally add debug symbols to ELF files made by quick compiler.

Added debug symbols to ELF files created by dex2oat using
the quick compiler. Adds two flags --include-debug-symbols and
--no-include-debug-symbols for dex2oat that control the inclusion of these
debug symbols. Debug info is added by default if kIsDebugBuild is true.

Fixed bug where Intel DWARF information would not correctly deal with
deduplicated code the binary.

Changed the portable compiler code path in dex2oat.cc so that symbols
will not be stripped when run with --include-debug-symbols.

Change-Id: Ia2eb2f654dedf0e5e8606f7744e05b8d14155fb1
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index fad6798..9903421 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -28,6 +28,7 @@
 #include "compiled_method.h"
 #include "compiler.h"
 #include "dex_file.h"
+#include "driver/compiler_options.h"
 #include "instruction_set.h"
 #include "invoke_type.h"
 #include "method_reference.h"
@@ -105,8 +106,7 @@
                           InstructionSetFeatures instruction_set_features,
                           bool image, DescriptorSet* image_classes,
                           size_t thread_count, bool dump_stats, bool dump_passes,
-                          CumulativeLogger* timer,
-                          std::string profile_file = "");
+                          CumulativeLogger* timer, std::string profile_file = "");
 
   ~CompilerDriver();
 
@@ -394,6 +394,10 @@
     return dump_passes_;
   }
 
+  bool DidIncludeDebugSymbols() const {
+    return compiler_options_->GetIncludeDebugSymbols();
+  }
+
   CumulativeLogger* GetTimingsLogger() const {
     return timings_logger_;
   }
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 05a9ac7..5d1c5da 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -42,6 +42,7 @@
   static const size_t kDefaultTinyMethodThreshold = 20;
   static const size_t kDefaultNumDexMethodsThreshold = 900;
   static constexpr double kDefaultTopKProfileThreshold = 90.0;
+  static const bool kDefaultIncludeDebugSymbols = kIsDebugBuild;
 
   CompilerOptions() :
     compiler_filter_(kDefaultCompilerFilter),
@@ -51,7 +52,8 @@
     tiny_method_threshold_(kDefaultTinyMethodThreshold),
     num_dex_methods_threshold_(kDefaultNumDexMethodsThreshold),
     generate_gdb_information_(false),
-    top_k_profile_threshold_(kDefaultTopKProfileThreshold)
+    top_k_profile_threshold_(kDefaultTopKProfileThreshold),
+    include_debug_symbols_(kDefaultIncludeDebugSymbols)
 #ifdef ART_SEA_IR_MODE
     , sea_ir_mode_(false)
 #endif
@@ -64,7 +66,8 @@
                   size_t tiny_method_threshold,
                   size_t num_dex_methods_threshold,
                   bool generate_gdb_information,
-                  double top_k_profile_threshold
+                  double top_k_profile_threshold,
+                  bool include_debug_symbols
 #ifdef ART_SEA_IR_MODE
                   , bool sea_ir_mode
 #endif
@@ -76,7 +79,8 @@
     tiny_method_threshold_(tiny_method_threshold),
     num_dex_methods_threshold_(num_dex_methods_threshold),
     generate_gdb_information_(generate_gdb_information),
-    top_k_profile_threshold_(top_k_profile_threshold)
+    top_k_profile_threshold_(top_k_profile_threshold),
+    include_debug_symbols_(include_debug_symbols)
 #ifdef ART_SEA_IR_MODE
     , sea_ir_mode_(sea_ir_mode)
 #endif
@@ -139,6 +143,10 @@
     return top_k_profile_threshold_;
   }
 
+  bool GetIncludeDebugSymbols() const {
+    return include_debug_symbols_;
+  }
+
 #ifdef ART_SEA_IR_MODE
   bool GetSeaIrMode();
 #endif
@@ -157,6 +165,7 @@
   bool generate_gdb_information_;
   // When using a profile file only the top K% of the profiled samples will be compiled.
   double top_k_profile_threshold_;
+  bool include_debug_symbols_;
 #ifdef ART_SEA_IR_MODE
   bool sea_ir_mode_;
 #endif
diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc
index cb66e48..78757ec 100644
--- a/compiler/elf_writer_quick.cc
+++ b/compiler/elf_writer_quick.cc
@@ -807,12 +807,17 @@
                            const std::string& android_root_unused,
                            bool is_host_unused) {
   const bool debug = false;
+  const bool add_symbols = oat_writer->DidAddSymbols();
   const OatHeader& oat_header = oat_writer->GetOatHeader();
   Elf32_Word oat_data_size = oat_header.GetExecutableOffset();
   uint32_t oat_exec_size = oat_writer->GetSize() - oat_data_size;
 
   ElfBuilder builder(oat_writer, elf_file_, compiler_driver_->GetInstructionSet(), 0,
-                     oat_data_size, oat_data_size, oat_exec_size, false, debug);
+                     oat_data_size, oat_data_size, oat_exec_size, add_symbols, debug);
+
+  if (add_symbols) {
+    AddDebugSymbols(builder, oat_writer, debug);
+  }
 
   bool generateDebugInformation = compiler_driver_->GetCallFrameInformation() != nullptr;
   if (generateDebugInformation) {
@@ -833,6 +838,15 @@
   return builder.Write();
 }
 
+void ElfWriterQuick::AddDebugSymbols(ElfBuilder& builder, OatWriter* oat_writer, bool debug) {
+  const std::vector<OatWriter::DebugInfo>& method_info = oat_writer->GetCFIMethodInfo();
+  ElfSymtabBuilder* symtab = &builder.symtab_builder_;
+  for (auto it = method_info.begin(); it != method_info.end(); ++it) {
+    symtab->AddSymbol(it->method_name_, &builder.text_builder_, it->low_pc_, true,
+                      it->high_pc_ - it->low_pc_, STB_GLOBAL, STT_FUNC);
+  }
+}
+
 static void UpdateWord(std::vector<uint8_t>*buf, int offset, int data) {
   (*buf)[offset+0] = data;
   (*buf)[offset+1] = data >> 8;
diff --git a/compiler/elf_writer_quick.h b/compiler/elf_writer_quick.h
index f687d2e..dbdccfc 100644
--- a/compiler/elf_writer_quick.h
+++ b/compiler/elf_writer_quick.h
@@ -48,6 +48,10 @@
   ~ElfWriterQuick() {}
 
   class ElfBuilder;
+  void AddDebugSymbols(ElfBuilder& builder,
+                       OatWriter* oat_writer,
+                       bool debug);
+
   class ElfSectionBuilder {
    public:
     ElfSectionBuilder(const std::string& sec_name, Elf32_Word type, Elf32_Word flags,
@@ -235,7 +239,6 @@
     ~ElfBuilder() {}
 
     bool Write();
-    ElfSymtabBuilder* GetDefaultDynsymBuilder() { return &dynsym_builder_; }
 
     // Adds the given raw section to the builder. This will copy it. The caller
     // is responsible for deallocating their copy.
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 5d532ab..65bc318 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -350,31 +350,14 @@
         uint32_t thumb_offset = compiled_method->CodeDelta();
         quick_code_offset = offset_ + sizeof(OatQuickMethodHeader) + thumb_offset;
 
-        std::vector<uint8_t>* cfi_info = writer_->compiler_driver_->GetCallFrameInformation();
-        if (cfi_info != nullptr) {
-          // Copy in the FDE, if present
-          const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo();
-          if (fde != nullptr) {
-            // Copy the information into cfi_info and then fix the address in the new copy.
-            int cur_offset = cfi_info->size();
-            cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
-
-            // Set the 'initial_location' field to address the start of the method.
-            uint32_t new_value = quick_code_offset - writer_->oat_header_->GetExecutableOffset();
-            uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t);
-            (*cfi_info)[offset_to_update+0] = new_value;
-            (*cfi_info)[offset_to_update+1] = new_value >> 8;
-            (*cfi_info)[offset_to_update+2] = new_value >> 16;
-            (*cfi_info)[offset_to_update+3] = new_value >> 24;
-            std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, false);
-            writer_->method_info_.push_back(DebugInfo(name, new_value, new_value + code_size));
-          }
-        }
+        bool force_debug_capture = false;
+        bool deduped = false;
 
         // Deduplicate code arrays.
         auto code_iter = dedupe_map_.find(compiled_method);
         if (code_iter != dedupe_map_.end()) {
           quick_code_offset = code_iter->second;
+          deduped = true;
         } else {
           dedupe_map_.Put(compiled_method, quick_code_offset);
         }
@@ -409,6 +392,41 @@
           writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
           offset_ += code_size;
         }
+
+        uint32_t quick_code_start = quick_code_offset - writer_->oat_header_->GetExecutableOffset();
+        std::vector<uint8_t>* cfi_info = writer_->compiler_driver_->GetCallFrameInformation();
+        if (cfi_info != nullptr) {
+          // Copy in the FDE, if present
+          const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo();
+          if (fde != nullptr) {
+            // Copy the information into cfi_info and then fix the address in the new copy.
+            int cur_offset = cfi_info->size();
+            cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
+
+            // Set the 'initial_location' field to address the start of the method.
+            uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t);
+            (*cfi_info)[offset_to_update+0] = quick_code_start;
+            (*cfi_info)[offset_to_update+1] = quick_code_start >> 8;
+            (*cfi_info)[offset_to_update+2] = quick_code_start >> 16;
+            (*cfi_info)[offset_to_update+3] = quick_code_start >> 24;
+            force_debug_capture = true;
+          }
+        }
+
+
+        if (writer_->compiler_driver_->DidIncludeDebugSymbols() || force_debug_capture) {
+          // Record debug information for this function if we are doing that or
+          // we have CFI and so need it.
+          std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, true);
+          if (deduped) {
+            // TODO We should place the DEDUPED tag on the first instance of a
+            // deduplicated symbol so that it will show up in a debuggerd crash
+            // report.
+            name += " [ DEDUPED ]";
+          }
+          writer_->method_info_.push_back(DebugInfo(name, quick_code_start,
+                                                    quick_code_start + code_size));
+        }
       }
 
       if (kIsDebugBuild) {
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 8c20aa8..dbecb95 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -108,6 +108,10 @@
     return method_info_;
   }
 
+  bool DidAddSymbols() const {
+    return compiler_driver_->DidIncludeDebugSymbols();
+  }
+
  private:
   // The DataAccess classes are helper classes that provide access to members related to
   // a given map, i.e. GC map, mapping table or vmap table. By abstracting these away
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 35149cf..e2943d3 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -203,6 +203,10 @@
   UsageError("");
   UsageError("  --dump-timing: display a breakdown of where time was spent");
   UsageError("");
+  UsageError("  --include-debug-symbols: Include ELF symbols in this oat file");
+  UsageError("");
+  UsageError("  --no-include-debug-symbols: Do not include ELF symbols in this oat file");
+  UsageError("");
   UsageError("  --runtime-arg <argument>: used to specify various arguments for the runtime,");
   UsageError("      such as initial heap size, maximum heap size, and verbose output.");
   UsageError("      Use a separate --runtime-arg switch for each argument.");
@@ -816,6 +820,7 @@
   bool dump_stats = false;
   bool dump_timing = false;
   bool dump_passes = false;
+  bool include_debug_symbols = kIsDebugBuild;
   bool dump_slow_timing = kIsDebugBuild;
   bool watch_dog_enabled = !kIsTargetBuild;
   bool generate_gdb_information = kIsDebugBuild;
@@ -969,6 +974,10 @@
       dump_passes = true;
     } else if (option == "--dump-stats") {
       dump_stats = true;
+    } else if (option == "--include-debug-symbols" || option == "--no-strip-symbols") {
+      include_debug_symbols = true;
+    } else if (option == "--no-include-debug-symbols" || option == "--strip-symbols") {
+      include_debug_symbols = false;
     } else if (option.starts_with("--profile-file=")) {
       profile_file = option.substr(strlen("--profile-file=")).data();
       VLOG(compiler) << "dex2oat: profile file is " << profile_file;
@@ -1122,7 +1131,8 @@
                                    tiny_method_threshold,
                                    num_dex_methods_threshold,
                                    generate_gdb_information,
-                                   top_k_profile_threshold
+                                   top_k_profile_threshold,
+                                   include_debug_symbols
 #ifdef ART_SEA_IR_MODE
                                    , compiler_options.sea_ir_ = true;
 #endif
@@ -1409,16 +1419,20 @@
   }
 
 #if ART_USE_PORTABLE_COMPILER  // We currently only generate symbols on Portable
-  timings.NewSplit("dex2oat ElfStripper");
-  // Strip unneeded sections for target
-  off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET);
-  CHECK_EQ(0, seek_actual);
-  std::string error_msg;
-  CHECK(ElfStripper::Strip(oat_file.get(), &error_msg)) << error_msg;
+  if (!compiler_options.GetIncludeDebugSymbols()) {
+    timings.NewSplit("dex2oat ElfStripper");
+    // Strip unneeded sections for target
+    off_t seek_actual = lseek(oat_file->Fd(), 0, SEEK_SET);
+    CHECK_EQ(0, seek_actual);
+    std::string error_msg;
+    CHECK(ElfStripper::Strip(oat_file.get(), &error_msg)) << error_msg;
 
 
-  // We wrote the oat file successfully, and want to keep it.
-  VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location;
+    // We wrote the oat file successfully, and want to keep it.
+    VLOG(compiler) << "Oat file written successfully (stripped): " << oat_location;
+  } else {
+    VLOG(compiler) << "Oat file written successfully without stripping: " << oat_location;
+  }
 #endif  // ART_USE_PORTABLE_COMPILER
 
   timings.EndSplit();