David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #ifndef ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_ |
| 18 | #define ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_ |
| 19 | |
| 20 | #include <map> |
| 21 | |
| 22 | #include "arch/instruction_set.h" |
| 23 | #include "compiled_method.h" |
| 24 | #include "debug/dwarf/debug_info_entry_writer.h" |
| 25 | #include "debug/dwarf/register.h" |
| 26 | #include "debug/method_debug_info.h" |
| 27 | #include "stack_map.h" |
| 28 | |
| 29 | namespace art { |
| 30 | namespace debug { |
| 31 | using Reg = dwarf::Reg; |
| 32 | |
| 33 | static Reg GetDwarfCoreReg(InstructionSet isa, int machine_reg) { |
| 34 | switch (isa) { |
| 35 | case kArm: |
| 36 | case kThumb2: |
| 37 | return Reg::ArmCore(machine_reg); |
| 38 | case kArm64: |
| 39 | return Reg::Arm64Core(machine_reg); |
| 40 | case kX86: |
| 41 | return Reg::X86Core(machine_reg); |
| 42 | case kX86_64: |
| 43 | return Reg::X86_64Core(machine_reg); |
| 44 | case kMips: |
| 45 | return Reg::MipsCore(machine_reg); |
| 46 | case kMips64: |
| 47 | return Reg::Mips64Core(machine_reg); |
| 48 | case kNone: |
| 49 | LOG(FATAL) << "No instruction set"; |
| 50 | } |
| 51 | UNREACHABLE(); |
| 52 | } |
| 53 | |
| 54 | static Reg GetDwarfFpReg(InstructionSet isa, int machine_reg) { |
| 55 | switch (isa) { |
| 56 | case kArm: |
| 57 | case kThumb2: |
| 58 | return Reg::ArmFp(machine_reg); |
| 59 | case kArm64: |
| 60 | return Reg::Arm64Fp(machine_reg); |
| 61 | case kX86: |
| 62 | return Reg::X86Fp(machine_reg); |
| 63 | case kX86_64: |
| 64 | return Reg::X86_64Fp(machine_reg); |
| 65 | case kMips: |
| 66 | return Reg::MipsFp(machine_reg); |
| 67 | case kMips64: |
| 68 | return Reg::Mips64Fp(machine_reg); |
| 69 | case kNone: |
| 70 | LOG(FATAL) << "No instruction set"; |
| 71 | } |
| 72 | UNREACHABLE(); |
| 73 | } |
| 74 | |
| 75 | struct VariableLocation { |
| 76 | uint32_t low_pc; |
| 77 | uint32_t high_pc; |
| 78 | DexRegisterLocation reg_lo; // May be None if the location is unknown. |
| 79 | DexRegisterLocation reg_hi; // Most significant bits of 64-bit value. |
| 80 | }; |
| 81 | |
| 82 | // Get the location of given dex register (e.g. stack or machine register). |
| 83 | // Note that the location might be different based on the current pc. |
| 84 | // The result will cover all ranges where the variable is in scope. |
David Srbecky | bfd26cd | 2016-02-10 13:57:09 +0000 | [diff] [blame^] | 85 | // PCs corresponding to stackmap with dex register map are accurate, |
| 86 | // all other PCs are best-effort only. |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 87 | std::vector<VariableLocation> GetVariableLocations(const MethodDebugInfo* method_info, |
| 88 | uint16_t vreg, |
| 89 | bool is64bitValue, |
| 90 | uint32_t dex_pc_low, |
| 91 | uint32_t dex_pc_high) { |
| 92 | std::vector<VariableLocation> variable_locations; |
| 93 | |
| 94 | // Get stack maps sorted by pc (they might not be sorted internally). |
| 95 | const CodeInfo code_info(method_info->compiled_method->GetVmapTable().data()); |
| 96 | const StackMapEncoding encoding = code_info.ExtractEncoding(); |
| 97 | std::map<uint32_t, StackMap> stack_maps; |
| 98 | for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) { |
| 99 | StackMap stack_map = code_info.GetStackMapAt(s, encoding); |
| 100 | DCHECK(stack_map.IsValid()); |
| 101 | const uint32_t low_pc = method_info->low_pc + stack_map.GetNativePcOffset(encoding); |
| 102 | DCHECK_LE(low_pc, method_info->high_pc); |
| 103 | stack_maps.emplace(low_pc, stack_map); |
| 104 | } |
| 105 | |
| 106 | // Create entries for the requested register based on stack map data. |
| 107 | for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) { |
| 108 | const StackMap& stack_map = it->second; |
| 109 | const uint32_t low_pc = it->first; |
| 110 | auto next_it = it; |
| 111 | next_it++; |
| 112 | const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first |
| 113 | : method_info->high_pc; |
| 114 | DCHECK_LE(low_pc, high_pc); |
| 115 | if (low_pc == high_pc) { |
| 116 | continue; // Ignore if the address range is empty. |
| 117 | } |
| 118 | |
| 119 | // Check that the stack map is in the requested range. |
| 120 | uint32_t dex_pc = stack_map.GetDexPc(encoding); |
| 121 | if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) { |
| 122 | continue; |
| 123 | } |
| 124 | |
| 125 | // Find the location of the dex register. |
| 126 | DexRegisterLocation reg_lo = DexRegisterLocation::None(); |
| 127 | DexRegisterLocation reg_hi = DexRegisterLocation::None(); |
| 128 | if (stack_map.HasDexRegisterMap(encoding)) { |
| 129 | DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf( |
| 130 | stack_map, encoding, method_info->code_item->registers_size_); |
| 131 | reg_lo = dex_register_map.GetDexRegisterLocation( |
| 132 | vreg, method_info->code_item->registers_size_, code_info, encoding); |
| 133 | if (is64bitValue) { |
| 134 | reg_hi = dex_register_map.GetDexRegisterLocation( |
| 135 | vreg + 1, method_info->code_item->registers_size_, code_info, encoding); |
| 136 | } |
| 137 | } |
| 138 | |
| 139 | // Add location entry for this address range. |
| 140 | if (!variable_locations.empty() && |
| 141 | variable_locations.back().reg_lo == reg_lo && |
| 142 | variable_locations.back().reg_hi == reg_hi && |
| 143 | variable_locations.back().high_pc == low_pc) { |
| 144 | // Merge with the previous entry (extend its range). |
| 145 | variable_locations.back().high_pc = high_pc; |
David Srbecky | bfd26cd | 2016-02-10 13:57:09 +0000 | [diff] [blame^] | 146 | } else if (!variable_locations.empty() && reg_lo == DexRegisterLocation::None()) { |
| 147 | // Unknown location - use the last known location as best-effort guess. |
| 148 | variable_locations.back().high_pc = high_pc; |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 149 | } else { |
| 150 | variable_locations.push_back({low_pc, high_pc, reg_lo, reg_hi}); |
| 151 | } |
| 152 | } |
| 153 | |
| 154 | return variable_locations; |
| 155 | } |
| 156 | |
| 157 | // Write table into .debug_loc which describes location of dex register. |
| 158 | // The dex register might be valid only at some points and it might |
| 159 | // move between machine registers and stack. |
| 160 | static void WriteDebugLocEntry(const MethodDebugInfo* method_info, |
| 161 | uint16_t vreg, |
| 162 | bool is64bitValue, |
| 163 | uint32_t compilation_unit_low_pc, |
| 164 | uint32_t dex_pc_low, |
| 165 | uint32_t dex_pc_high, |
| 166 | InstructionSet isa, |
| 167 | dwarf::DebugInfoEntryWriter<>* debug_info, |
| 168 | std::vector<uint8_t>* debug_loc_buffer, |
| 169 | std::vector<uint8_t>* debug_ranges_buffer) { |
| 170 | using Kind = DexRegisterLocation::Kind; |
| 171 | if (!method_info->IsFromOptimizingCompiler()) { |
| 172 | return; |
| 173 | } |
| 174 | |
| 175 | dwarf::Writer<> debug_loc(debug_loc_buffer); |
| 176 | dwarf::Writer<> debug_ranges(debug_ranges_buffer); |
| 177 | debug_info->WriteSecOffset(dwarf::DW_AT_location, debug_loc.size()); |
| 178 | debug_info->WriteSecOffset(dwarf::DW_AT_start_scope, debug_ranges.size()); |
| 179 | |
| 180 | std::vector<VariableLocation> variable_locations = GetVariableLocations( |
| 181 | method_info, |
| 182 | vreg, |
| 183 | is64bitValue, |
| 184 | dex_pc_low, |
| 185 | dex_pc_high); |
| 186 | |
| 187 | // Write .debug_loc entries. |
| 188 | const bool is64bit = Is64BitInstructionSet(isa); |
| 189 | std::vector<uint8_t> expr_buffer; |
| 190 | for (const VariableLocation& variable_location : variable_locations) { |
| 191 | // Translate dex register location to DWARF expression. |
| 192 | // Note that 64-bit value might be split to two distinct locations. |
| 193 | // (for example, two 32-bit machine registers, or even stack and register) |
| 194 | dwarf::Expression expr(&expr_buffer); |
| 195 | DexRegisterLocation reg_lo = variable_location.reg_lo; |
| 196 | DexRegisterLocation reg_hi = variable_location.reg_hi; |
| 197 | for (int piece = 0; piece < (is64bitValue ? 2 : 1); piece++) { |
| 198 | DexRegisterLocation reg_loc = (piece == 0 ? reg_lo : reg_hi); |
| 199 | const Kind kind = reg_loc.GetKind(); |
| 200 | const int32_t value = reg_loc.GetValue(); |
| 201 | if (kind == Kind::kInStack) { |
| 202 | const size_t frame_size = method_info->compiled_method->GetFrameSizeInBytes(); |
| 203 | // The stack offset is relative to SP. Make it relative to CFA. |
| 204 | expr.WriteOpFbreg(value - frame_size); |
| 205 | if (piece == 0 && reg_hi.GetKind() == Kind::kInStack && |
| 206 | reg_hi.GetValue() == value + 4) { |
| 207 | break; // the high word is correctly implied by the low word. |
| 208 | } |
| 209 | } else if (kind == Kind::kInRegister) { |
| 210 | expr.WriteOpReg(GetDwarfCoreReg(isa, value).num()); |
| 211 | if (piece == 0 && reg_hi.GetKind() == Kind::kInRegisterHigh && |
| 212 | reg_hi.GetValue() == value) { |
| 213 | break; // the high word is correctly implied by the low word. |
| 214 | } |
| 215 | } else if (kind == Kind::kInFpuRegister) { |
| 216 | if ((isa == kArm || isa == kThumb2) && |
| 217 | piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegister && |
| 218 | reg_hi.GetValue() == value + 1 && value % 2 == 0) { |
| 219 | // Translate S register pair to D register (e.g. S4+S5 to D2). |
| 220 | expr.WriteOpReg(Reg::ArmDp(value / 2).num()); |
| 221 | break; |
| 222 | } |
| 223 | expr.WriteOpReg(GetDwarfFpReg(isa, value).num()); |
| 224 | if (piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegisterHigh && |
| 225 | reg_hi.GetValue() == reg_lo.GetValue()) { |
| 226 | break; // the high word is correctly implied by the low word. |
| 227 | } |
| 228 | } else if (kind == Kind::kConstant) { |
| 229 | expr.WriteOpConsts(value); |
| 230 | expr.WriteOpStackValue(); |
| 231 | } else if (kind == Kind::kNone) { |
| 232 | break; |
| 233 | } else { |
| 234 | // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind(). |
| 235 | // kInRegisterHigh and kInFpuRegisterHigh should be handled by |
| 236 | // the special cases above and they should not occur alone. |
| 237 | LOG(ERROR) << "Unexpected register location kind: " |
| 238 | << DexRegisterLocation::PrettyDescriptor(kind); |
| 239 | break; |
| 240 | } |
| 241 | if (is64bitValue) { |
| 242 | // Write the marker which is needed by split 64-bit values. |
| 243 | // This code is skipped by the special cases. |
| 244 | expr.WriteOpPiece(4); |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | if (expr.size() > 0) { |
| 249 | if (is64bit) { |
| 250 | debug_loc.PushUint64(variable_location.low_pc - compilation_unit_low_pc); |
| 251 | debug_loc.PushUint64(variable_location.high_pc - compilation_unit_low_pc); |
| 252 | } else { |
| 253 | debug_loc.PushUint32(variable_location.low_pc - compilation_unit_low_pc); |
| 254 | debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc); |
| 255 | } |
| 256 | // Write the expression. |
| 257 | debug_loc.PushUint16(expr.size()); |
| 258 | debug_loc.PushData(expr.data()); |
| 259 | } else { |
| 260 | // Do not generate .debug_loc if the location is not known. |
| 261 | } |
| 262 | } |
| 263 | // Write end-of-list entry. |
| 264 | if (is64bit) { |
| 265 | debug_loc.PushUint64(0); |
| 266 | debug_loc.PushUint64(0); |
| 267 | } else { |
| 268 | debug_loc.PushUint32(0); |
| 269 | debug_loc.PushUint32(0); |
| 270 | } |
| 271 | |
| 272 | // Write .debug_ranges entries. |
| 273 | // This includes ranges where the variable is in scope but the location is not known. |
| 274 | for (size_t i = 0; i < variable_locations.size(); i++) { |
| 275 | uint32_t low_pc = variable_locations[i].low_pc; |
| 276 | uint32_t high_pc = variable_locations[i].high_pc; |
| 277 | while (i + 1 < variable_locations.size() && variable_locations[i+1].low_pc == high_pc) { |
| 278 | // Merge address range with the next entry. |
| 279 | high_pc = variable_locations[++i].high_pc; |
| 280 | } |
| 281 | if (is64bit) { |
| 282 | debug_ranges.PushUint64(low_pc - compilation_unit_low_pc); |
| 283 | debug_ranges.PushUint64(high_pc - compilation_unit_low_pc); |
| 284 | } else { |
| 285 | debug_ranges.PushUint32(low_pc - compilation_unit_low_pc); |
| 286 | debug_ranges.PushUint32(high_pc - compilation_unit_low_pc); |
| 287 | } |
| 288 | } |
| 289 | // Write end-of-list entry. |
| 290 | if (is64bit) { |
| 291 | debug_ranges.PushUint64(0); |
| 292 | debug_ranges.PushUint64(0); |
| 293 | } else { |
| 294 | debug_ranges.PushUint32(0); |
| 295 | debug_ranges.PushUint32(0); |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | } // namespace debug |
| 300 | } // namespace art |
| 301 | |
| 302 | #endif // ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_ |
| 303 | |