Generate more stack maps during native debugging.
Generate extra stack map at the start of each java statement.
The stack maps are later translated to DWARF which allows
LLDB to set breakpoints and view local variables.
Change-Id: If00ab875513308e4a1399d1e12e0fe8934a6f0c3
diff --git a/compiler/driver/compiler_options.cc b/compiler/driver/compiler_options.cc
index 4d2d924..8c38cf2 100644
--- a/compiler/driver/compiler_options.cc
+++ b/compiler/driver/compiler_options.cc
@@ -34,6 +34,7 @@
include_patch_information_(kDefaultIncludePatchInformation),
top_k_profile_threshold_(kDefaultTopKProfileThreshold),
debuggable_(false),
+ native_debuggable_(kDefaultNativeDebuggable),
generate_debug_info_(kDefaultGenerateDebugInfo),
implicit_null_checks_(true),
implicit_so_checks_(true),
@@ -81,6 +82,7 @@
include_patch_information_(include_patch_information),
top_k_profile_threshold_(top_k_profile_threshold),
debuggable_(debuggable),
+ native_debuggable_(kDefaultNativeDebuggable),
generate_debug_info_(generate_debug_info),
implicit_null_checks_(implicit_null_checks),
implicit_so_checks_(implicit_so_checks),
@@ -207,6 +209,10 @@
} else if (option == "--debuggable") {
debuggable_ = true;
generate_debug_info_ = true;
+ } else if (option == "--native-debuggable") {
+ native_debuggable_ = true;
+ debuggable_ = true;
+ generate_debug_info_ = true;
} else if (option.starts_with("--top-k-profile-threshold=")) {
ParseDouble(option.data(), '=', 0.0, 100.0, &top_k_profile_threshold_, Usage);
} else if (option == "--include-patch-information") {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index e6acab4..2b047a2 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -49,6 +49,7 @@
static const size_t kDefaultTinyMethodThreshold = 20;
static const size_t kDefaultNumDexMethodsThreshold = 900;
static constexpr double kDefaultTopKProfileThreshold = 90.0;
+ static const bool kDefaultNativeDebuggable = false;
static const bool kDefaultGenerateDebugInfo = kIsDebugBuild;
static const bool kDefaultIncludePatchInformation = false;
static const size_t kDefaultInlineDepthLimit = 3;
@@ -162,6 +163,10 @@
return debuggable_;
}
+ bool GetNativeDebuggable() const {
+ return native_debuggable_;
+ }
+
bool GetGenerateDebugInfo() const {
return generate_debug_info_;
}
@@ -240,6 +245,7 @@
// When using a profile file only the top K% of the profiled samples will be compiled.
double top_k_profile_threshold_;
bool debuggable_;
+ bool native_debuggable_;
bool generate_debug_info_;
bool implicit_null_checks_;
bool implicit_so_checks_;
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 1178d0f..4dd0d26 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -17,6 +17,8 @@
#include "builder.h"
#include "art_field-inl.h"
+#include "base/arena_bit_vector.h"
+#include "base/bit_vector-inl.h"
#include "base/logging.h"
#include "class_linker.h"
#include "dex/verified_method.h"
@@ -458,6 +460,19 @@
return false;
}
+ // Find locations where we want to generate extra stackmaps for native debugging.
+ // This allows us to generate the info only at interesting points (for example,
+ // at start of java statement) rather than before every dex instruction.
+ const bool native_debuggable = compiler_driver_ != nullptr &&
+ compiler_driver_->GetCompilerOptions().GetNativeDebuggable();
+ ArenaBitVector* native_debug_info_locations;
+ if (native_debuggable) {
+ const uint32_t num_instructions = code_item.insns_size_in_code_units_;
+ native_debug_info_locations = new (arena_) ArenaBitVector (arena_, num_instructions, false);
+ native_debug_info_locations->ClearAllBits();
+ FindNativeDebugInfoLocations(code_item, native_debug_info_locations);
+ }
+
CreateBlocksForTryCatch(code_item);
InitializeParameters(code_item.ins_size_);
@@ -467,6 +482,11 @@
// Update the current block if dex_pc starts a new block.
MaybeUpdateCurrentBlock(dex_pc);
const Instruction& instruction = *Instruction::At(code_ptr);
+ if (native_debuggable && native_debug_info_locations->IsBitSet(dex_pc)) {
+ if (current_block_ != nullptr) {
+ current_block_->AddInstruction(new (arena_) HNativeDebugInfo(dex_pc));
+ }
+ }
if (!AnalyzeDexInstruction(instruction, dex_pc)) {
return false;
}
@@ -507,6 +527,47 @@
current_block_ = block;
}
+void HGraphBuilder::FindNativeDebugInfoLocations(const DexFile::CodeItem& code_item,
+ ArenaBitVector* locations) {
+ // The callback gets called when the line number changes.
+ // In other words, it marks the start of new java statement.
+ struct Callback {
+ static bool Position(void* ctx, const DexFile::PositionInfo& entry) {
+ static_cast<ArenaBitVector*>(ctx)->SetBit(entry.address_);
+ return false;
+ }
+ };
+ dex_file_->DecodeDebugPositionInfo(&code_item, Callback::Position, locations);
+ // Add native debug info at the start of every basic block.
+ for (uint32_t pc = 0; pc < code_item.insns_size_in_code_units_; pc++) {
+ if (FindBlockStartingAt(pc) != nullptr) {
+ locations->SetBit(pc);
+ }
+ }
+ // Instruction-specific tweaks.
+ const Instruction* const begin = Instruction::At(code_item.insns_);
+ const Instruction* const end = begin->RelativeAt(code_item.insns_size_in_code_units_);
+ for (const Instruction* inst = begin; inst < end; inst = inst->Next()) {
+ switch (inst->Opcode()) {
+ case Instruction::MOVE_EXCEPTION:
+ case Instruction::MOVE_RESULT:
+ case Instruction::MOVE_RESULT_WIDE:
+ case Instruction::MOVE_RESULT_OBJECT: {
+ // The compiler checks that there are no instructions before those.
+ // So generate HNativeDebugInfo after them instead.
+ locations->ClearBit(inst->GetDexPc(code_item.insns_));
+ const Instruction* next = inst->Next();
+ if (next < end) {
+ locations->SetBit(next->GetDexPc(code_item.insns_));
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
bool HGraphBuilder::ComputeBranchTargets(const uint16_t* code_ptr,
const uint16_t* code_end,
size_t* number_of_branches) {
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 73e85bb..26bf1cb 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -112,6 +112,7 @@
const uint16_t* end,
size_t* number_of_branches);
void MaybeUpdateCurrentBlock(size_t dex_pc);
+ void FindNativeDebugInfoLocations(const DexFile::CodeItem& code_item, ArenaBitVector* locations);
HBasicBlock* FindBlockStartingAt(int32_t dex_pc) const;
HBasicBlock* FindOrCreateBlockStartingAt(int32_t dex_pc);
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 07cc059..58feb67 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -1618,6 +1618,14 @@
/* false_target */ nullptr);
}
+void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ new (GetGraph()->GetArena()) LocationSummary(info);
+}
+
+void InstructionCodeGeneratorARM::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ codegen_->RecordPcInfo(info, info->GetDexPc());
+}
+
void LocationsBuilderARM::HandleCondition(HCondition* cond) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(cond, LocationSummary::kNoCall);
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 068676d..b49f42b 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2949,6 +2949,14 @@
/* false_target */ nullptr);
}
+void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ new (GetGraph()->GetArena()) LocationSummary(info);
+}
+
+void InstructionCodeGeneratorARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ codegen_->RecordPcInfo(info, info->GetDexPc());
+}
+
void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) {
HandleFieldGet(instruction);
}
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index ef6b403..07efdee 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -3244,6 +3244,14 @@
/* false_target */ nullptr);
}
+void LocationsBuilderMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ new (GetGraph()->GetArena()) LocationSummary(info);
+}
+
+void InstructionCodeGeneratorMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ codegen_->RecordPcInfo(info, info->GetDexPc());
+}
+
void LocationsBuilderMIPS::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) {
Primitive::Type field_type = field_info.GetFieldType();
bool is_wide = (field_type == Primitive::kPrimLong) || (field_type == Primitive::kPrimDouble);
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 0f340a9..05834ff 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -2745,6 +2745,14 @@
/* false_target */ nullptr);
}
+void LocationsBuilderMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ new (GetGraph()->GetArena()) LocationSummary(info);
+}
+
+void InstructionCodeGeneratorMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ codegen_->RecordPcInfo(info, info->GetDexPc());
+}
+
void LocationsBuilderMIPS64::HandleFieldGet(HInstruction* instruction,
const FieldInfo& field_info ATTRIBUTE_UNUSED) {
LocationSummary* locations =
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 3136109..fd18917 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -1616,6 +1616,14 @@
/* false_target */ nullptr);
}
+void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ new (GetGraph()->GetArena()) LocationSummary(info);
+}
+
+void InstructionCodeGeneratorX86::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ codegen_->RecordPcInfo(info, info->GetDexPc());
+}
+
void LocationsBuilderX86::VisitLocal(HLocal* local) {
local->SetLocations(nullptr);
}
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index 0de616c..ffd8c42 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1600,6 +1600,14 @@
/* false_target */ nullptr);
}
+void LocationsBuilderX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ new (GetGraph()->GetArena()) LocationSummary(info);
+}
+
+void InstructionCodeGeneratorX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) {
+ codegen_->RecordPcInfo(info, info->GetDexPc());
+}
+
void LocationsBuilderX86_64::VisitLocal(HLocal* local) {
local->SetLocations(nullptr);
}
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index 02e5dab..67ff87a 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -165,6 +165,7 @@
if (!inst->HasSideEffects()
&& !inst->CanThrow()
&& !inst->IsSuspendCheck()
+ && !inst->IsNativeDebugInfo()
// If we added an explicit barrier then we should keep it.
&& !inst->IsMemoryBarrier()
&& !inst->IsParameterValue()
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 00820a6..db3e969 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1066,6 +1066,7 @@
M(MemoryBarrier, Instruction) \
M(MonitorOperation, Instruction) \
M(Mul, BinaryOperation) \
+ M(NativeDebugInfo, Instruction) \
M(Neg, UnaryOperation) \
M(NewArray, Instruction) \
M(NewInstance, Instruction) \
@@ -4872,6 +4873,23 @@
DISALLOW_COPY_AND_ASSIGN(HSuspendCheck);
};
+// Pseudo-instruction which provides the native debugger with mapping information.
+// It ensures that we can generate line number and local variables at this point.
+class HNativeDebugInfo : public HTemplateInstruction<0> {
+ public:
+ explicit HNativeDebugInfo(uint32_t dex_pc)
+ : HTemplateInstruction<0>(SideEffects::None(), dex_pc) {}
+
+ bool NeedsEnvironment() const OVERRIDE {
+ return true;
+ }
+
+ DECLARE_INSTRUCTION(NativeDebugInfo);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HNativeDebugInfo);
+};
+
/**
* Instruction to load a Class object.
*/
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 6fae8e4..21ce73c 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -315,10 +315,13 @@
UsageError(" stripped using standard command line tools such as strip or objcopy.");
UsageError(" (enabled by default in debug builds, disabled by default otherwise)");
UsageError("");
- UsageError(" --debuggable: Produce debuggable code. Implies --generate-debug-info.");
- UsageError("");
UsageError(" --no-generate-debug-info: Do not generate debug information for native debugging.");
UsageError("");
+ UsageError(" --debuggable: Produce code debuggable with Java debugger. Implies -g.");
+ UsageError("");
+ UsageError(" --native-debuggable: Produce code debuggable with native debugger (like LLDB).");
+ UsageError(" Implies --debuggable.");
+ 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.");