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