Split elf_writer_debug.cc to several files.
Refactoring only. The file has grown significantly over time,
and it is time to split it so it can be better managed.
Change-Id: Idce0231718add722292f4701df353d5baf31de5f
diff --git a/compiler/debug/elf_debug_loc_writer.h b/compiler/debug/elf_debug_loc_writer.h
new file mode 100644
index 0000000..fd7f949
--- /dev/null
+++ b/compiler/debug/elf_debug_loc_writer.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+#ifndef ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_
+#define ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_
+
+#include <map>
+
+#include "arch/instruction_set.h"
+#include "compiled_method.h"
+#include "debug/dwarf/debug_info_entry_writer.h"
+#include "debug/dwarf/register.h"
+#include "debug/method_debug_info.h"
+#include "stack_map.h"
+
+namespace art {
+namespace debug {
+using Reg = dwarf::Reg;
+
+static Reg GetDwarfCoreReg(InstructionSet isa, int machine_reg) {
+ switch (isa) {
+ case kArm:
+ case kThumb2:
+ return Reg::ArmCore(machine_reg);
+ case kArm64:
+ return Reg::Arm64Core(machine_reg);
+ case kX86:
+ return Reg::X86Core(machine_reg);
+ case kX86_64:
+ return Reg::X86_64Core(machine_reg);
+ case kMips:
+ return Reg::MipsCore(machine_reg);
+ case kMips64:
+ return Reg::Mips64Core(machine_reg);
+ case kNone:
+ LOG(FATAL) << "No instruction set";
+ }
+ UNREACHABLE();
+}
+
+static Reg GetDwarfFpReg(InstructionSet isa, int machine_reg) {
+ switch (isa) {
+ case kArm:
+ case kThumb2:
+ return Reg::ArmFp(machine_reg);
+ case kArm64:
+ return Reg::Arm64Fp(machine_reg);
+ case kX86:
+ return Reg::X86Fp(machine_reg);
+ case kX86_64:
+ return Reg::X86_64Fp(machine_reg);
+ case kMips:
+ return Reg::MipsFp(machine_reg);
+ case kMips64:
+ return Reg::Mips64Fp(machine_reg);
+ case kNone:
+ LOG(FATAL) << "No instruction set";
+ }
+ UNREACHABLE();
+}
+
+struct VariableLocation {
+ uint32_t low_pc;
+ uint32_t high_pc;
+ DexRegisterLocation reg_lo; // May be None if the location is unknown.
+ DexRegisterLocation reg_hi; // Most significant bits of 64-bit value.
+};
+
+// Get the location of given dex register (e.g. stack or machine register).
+// Note that the location might be different based on the current pc.
+// The result will cover all ranges where the variable is in scope.
+std::vector<VariableLocation> GetVariableLocations(const MethodDebugInfo* method_info,
+ uint16_t vreg,
+ bool is64bitValue,
+ uint32_t dex_pc_low,
+ uint32_t dex_pc_high) {
+ std::vector<VariableLocation> variable_locations;
+
+ // Get stack maps sorted by pc (they might not be sorted internally).
+ const CodeInfo code_info(method_info->compiled_method->GetVmapTable().data());
+ const StackMapEncoding encoding = code_info.ExtractEncoding();
+ std::map<uint32_t, StackMap> stack_maps;
+ for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
+ StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+ DCHECK(stack_map.IsValid());
+ const uint32_t low_pc = method_info->low_pc + stack_map.GetNativePcOffset(encoding);
+ DCHECK_LE(low_pc, method_info->high_pc);
+ stack_maps.emplace(low_pc, stack_map);
+ }
+
+ // Create entries for the requested register based on stack map data.
+ for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) {
+ const StackMap& stack_map = it->second;
+ const uint32_t low_pc = it->first;
+ auto next_it = it;
+ next_it++;
+ const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first
+ : method_info->high_pc;
+ DCHECK_LE(low_pc, high_pc);
+ if (low_pc == high_pc) {
+ continue; // Ignore if the address range is empty.
+ }
+
+ // Check that the stack map is in the requested range.
+ uint32_t dex_pc = stack_map.GetDexPc(encoding);
+ if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) {
+ continue;
+ }
+
+ // Find the location of the dex register.
+ DexRegisterLocation reg_lo = DexRegisterLocation::None();
+ DexRegisterLocation reg_hi = DexRegisterLocation::None();
+ if (stack_map.HasDexRegisterMap(encoding)) {
+ DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
+ stack_map, encoding, method_info->code_item->registers_size_);
+ reg_lo = dex_register_map.GetDexRegisterLocation(
+ vreg, method_info->code_item->registers_size_, code_info, encoding);
+ if (is64bitValue) {
+ reg_hi = dex_register_map.GetDexRegisterLocation(
+ vreg + 1, method_info->code_item->registers_size_, code_info, encoding);
+ }
+ }
+
+ // Add location entry for this address range.
+ if (!variable_locations.empty() &&
+ variable_locations.back().reg_lo == reg_lo &&
+ variable_locations.back().reg_hi == reg_hi &&
+ variable_locations.back().high_pc == low_pc) {
+ // Merge with the previous entry (extend its range).
+ variable_locations.back().high_pc = high_pc;
+ } else {
+ variable_locations.push_back({low_pc, high_pc, reg_lo, reg_hi});
+ }
+ }
+
+ return variable_locations;
+}
+
+// Write table into .debug_loc which describes location of dex register.
+// The dex register might be valid only at some points and it might
+// move between machine registers and stack.
+static void WriteDebugLocEntry(const MethodDebugInfo* method_info,
+ uint16_t vreg,
+ bool is64bitValue,
+ uint32_t compilation_unit_low_pc,
+ uint32_t dex_pc_low,
+ uint32_t dex_pc_high,
+ InstructionSet isa,
+ dwarf::DebugInfoEntryWriter<>* debug_info,
+ std::vector<uint8_t>* debug_loc_buffer,
+ std::vector<uint8_t>* debug_ranges_buffer) {
+ using Kind = DexRegisterLocation::Kind;
+ if (!method_info->IsFromOptimizingCompiler()) {
+ return;
+ }
+
+ dwarf::Writer<> debug_loc(debug_loc_buffer);
+ dwarf::Writer<> debug_ranges(debug_ranges_buffer);
+ debug_info->WriteSecOffset(dwarf::DW_AT_location, debug_loc.size());
+ debug_info->WriteSecOffset(dwarf::DW_AT_start_scope, debug_ranges.size());
+
+ std::vector<VariableLocation> variable_locations = GetVariableLocations(
+ method_info,
+ vreg,
+ is64bitValue,
+ dex_pc_low,
+ dex_pc_high);
+
+ // Write .debug_loc entries.
+ const bool is64bit = Is64BitInstructionSet(isa);
+ std::vector<uint8_t> expr_buffer;
+ for (const VariableLocation& variable_location : variable_locations) {
+ // Translate dex register location to DWARF expression.
+ // Note that 64-bit value might be split to two distinct locations.
+ // (for example, two 32-bit machine registers, or even stack and register)
+ dwarf::Expression expr(&expr_buffer);
+ DexRegisterLocation reg_lo = variable_location.reg_lo;
+ DexRegisterLocation reg_hi = variable_location.reg_hi;
+ for (int piece = 0; piece < (is64bitValue ? 2 : 1); piece++) {
+ DexRegisterLocation reg_loc = (piece == 0 ? reg_lo : reg_hi);
+ const Kind kind = reg_loc.GetKind();
+ const int32_t value = reg_loc.GetValue();
+ if (kind == Kind::kInStack) {
+ const size_t frame_size = method_info->compiled_method->GetFrameSizeInBytes();
+ // The stack offset is relative to SP. Make it relative to CFA.
+ expr.WriteOpFbreg(value - frame_size);
+ if (piece == 0 && reg_hi.GetKind() == Kind::kInStack &&
+ reg_hi.GetValue() == value + 4) {
+ break; // the high word is correctly implied by the low word.
+ }
+ } else if (kind == Kind::kInRegister) {
+ expr.WriteOpReg(GetDwarfCoreReg(isa, value).num());
+ if (piece == 0 && reg_hi.GetKind() == Kind::kInRegisterHigh &&
+ reg_hi.GetValue() == value) {
+ break; // the high word is correctly implied by the low word.
+ }
+ } else if (kind == Kind::kInFpuRegister) {
+ if ((isa == kArm || isa == kThumb2) &&
+ piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegister &&
+ reg_hi.GetValue() == value + 1 && value % 2 == 0) {
+ // Translate S register pair to D register (e.g. S4+S5 to D2).
+ expr.WriteOpReg(Reg::ArmDp(value / 2).num());
+ break;
+ }
+ expr.WriteOpReg(GetDwarfFpReg(isa, value).num());
+ if (piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegisterHigh &&
+ reg_hi.GetValue() == reg_lo.GetValue()) {
+ break; // the high word is correctly implied by the low word.
+ }
+ } else if (kind == Kind::kConstant) {
+ expr.WriteOpConsts(value);
+ expr.WriteOpStackValue();
+ } else if (kind == Kind::kNone) {
+ break;
+ } else {
+ // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind().
+ // kInRegisterHigh and kInFpuRegisterHigh should be handled by
+ // the special cases above and they should not occur alone.
+ LOG(ERROR) << "Unexpected register location kind: "
+ << DexRegisterLocation::PrettyDescriptor(kind);
+ break;
+ }
+ if (is64bitValue) {
+ // Write the marker which is needed by split 64-bit values.
+ // This code is skipped by the special cases.
+ expr.WriteOpPiece(4);
+ }
+ }
+
+ if (expr.size() > 0) {
+ if (is64bit) {
+ debug_loc.PushUint64(variable_location.low_pc - compilation_unit_low_pc);
+ debug_loc.PushUint64(variable_location.high_pc - compilation_unit_low_pc);
+ } else {
+ debug_loc.PushUint32(variable_location.low_pc - compilation_unit_low_pc);
+ debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc);
+ }
+ // Write the expression.
+ debug_loc.PushUint16(expr.size());
+ debug_loc.PushData(expr.data());
+ } else {
+ // Do not generate .debug_loc if the location is not known.
+ }
+ }
+ // Write end-of-list entry.
+ if (is64bit) {
+ debug_loc.PushUint64(0);
+ debug_loc.PushUint64(0);
+ } else {
+ debug_loc.PushUint32(0);
+ debug_loc.PushUint32(0);
+ }
+
+ // Write .debug_ranges entries.
+ // This includes ranges where the variable is in scope but the location is not known.
+ for (size_t i = 0; i < variable_locations.size(); i++) {
+ uint32_t low_pc = variable_locations[i].low_pc;
+ uint32_t high_pc = variable_locations[i].high_pc;
+ while (i + 1 < variable_locations.size() && variable_locations[i+1].low_pc == high_pc) {
+ // Merge address range with the next entry.
+ high_pc = variable_locations[++i].high_pc;
+ }
+ if (is64bit) {
+ debug_ranges.PushUint64(low_pc - compilation_unit_low_pc);
+ debug_ranges.PushUint64(high_pc - compilation_unit_low_pc);
+ } else {
+ debug_ranges.PushUint32(low_pc - compilation_unit_low_pc);
+ debug_ranges.PushUint32(high_pc - compilation_unit_low_pc);
+ }
+ }
+ // Write end-of-list entry.
+ if (is64bit) {
+ debug_ranges.PushUint64(0);
+ debug_ranges.PushUint64(0);
+ } else {
+ debug_ranges.PushUint32(0);
+ debug_ranges.PushUint32(0);
+ }
+}
+
+} // namespace debug
+} // namespace art
+
+#endif // ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_
+