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 | d89f605 | 2016-03-12 21:10:04 +0000 | [diff] [blame^] | 99 | // TODO(dsrbecky) Remove this once stackmaps get sorted by pc. |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 100 | const CodeInfo code_info(method_info->code_info); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 101 | const StackMapEncoding encoding = code_info.ExtractEncoding(); |
David Srbecky | 2ed15b6 | 2016-03-04 11:34:46 +0000 | [diff] [blame] | 102 | 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] | 103 | for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) { |
| 104 | StackMap stack_map = code_info.GetStackMapAt(s, encoding); |
| 105 | DCHECK(stack_map.IsValid()); |
David Srbecky | d89f605 | 2016-03-12 21:10:04 +0000 | [diff] [blame^] | 106 | if (!stack_map.HasDexRegisterMap(encoding)) { |
| 107 | // The compiler creates stackmaps without register maps at the start of |
| 108 | // basic blocks in order to keep instruction-accurate line number mapping. |
| 109 | // However, we never stop at those (breakpoint locations always have map). |
| 110 | // Therefore, for the purpose of local variables, we ignore them. |
| 111 | // The main reason for this is to save space by avoiding undefined gaps. |
| 112 | continue; |
| 113 | } |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 114 | const uint32_t pc_offset = stack_map.GetNativePcOffset(encoding); |
| 115 | DCHECK_LE(pc_offset, method_info->code_size); |
| 116 | DCHECK_LE(compilation_unit_code_address, method_info->code_address); |
| 117 | const uint32_t low_pc = dchecked_integral_cast<uint32_t>( |
| 118 | method_info->code_address + pc_offset - compilation_unit_code_address); |
David Srbecky | 2ed15b6 | 2016-03-04 11:34:46 +0000 | [diff] [blame] | 119 | stack_maps.emplace(low_pc, s); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 120 | } |
| 121 | |
| 122 | // Create entries for the requested register based on stack map data. |
| 123 | for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) { |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 124 | const uint32_t low_pc = it->first; |
David Srbecky | 2ed15b6 | 2016-03-04 11:34:46 +0000 | [diff] [blame] | 125 | const uint32_t stack_map_index = it->second; |
| 126 | const StackMap& stack_map = code_info.GetStackMapAt(stack_map_index, encoding); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 127 | auto next_it = it; |
| 128 | next_it++; |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 129 | const uint32_t high_pc = next_it != stack_maps.end() |
| 130 | ? next_it->first |
| 131 | : method_info->code_address + method_info->code_size - compilation_unit_code_address; |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 132 | DCHECK_LE(low_pc, high_pc); |
| 133 | if (low_pc == high_pc) { |
| 134 | continue; // Ignore if the address range is empty. |
| 135 | } |
| 136 | |
| 137 | // Check that the stack map is in the requested range. |
| 138 | uint32_t dex_pc = stack_map.GetDexPc(encoding); |
| 139 | if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) { |
David Srbecky | d89f605 | 2016-03-12 21:10:04 +0000 | [diff] [blame^] | 140 | // The variable is not in scope at this PC. Therefore omit the entry. |
| 141 | // Note that this is different to None() entry which means in scope, but unknown location. |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 142 | continue; |
| 143 | } |
| 144 | |
| 145 | // Find the location of the dex register. |
| 146 | DexRegisterLocation reg_lo = DexRegisterLocation::None(); |
| 147 | DexRegisterLocation reg_hi = DexRegisterLocation::None(); |
David Srbecky | 2ed15b6 | 2016-03-04 11:34:46 +0000 | [diff] [blame] | 148 | DCHECK_LT(stack_map_index, dex_register_maps.size()); |
| 149 | DexRegisterMap dex_register_map = dex_register_maps[stack_map_index]; |
David Srbecky | d89f605 | 2016-03-12 21:10:04 +0000 | [diff] [blame^] | 150 | DCHECK(dex_register_map.IsValid()); |
| 151 | reg_lo = dex_register_map.GetDexRegisterLocation( |
| 152 | vreg, method_info->code_item->registers_size_, code_info, encoding); |
| 153 | if (is64bitValue) { |
| 154 | reg_hi = dex_register_map.GetDexRegisterLocation( |
| 155 | vreg + 1, method_info->code_item->registers_size_, code_info, encoding); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 156 | } |
| 157 | |
| 158 | // Add location entry for this address range. |
| 159 | if (!variable_locations.empty() && |
| 160 | variable_locations.back().reg_lo == reg_lo && |
| 161 | variable_locations.back().reg_hi == reg_hi && |
| 162 | variable_locations.back().high_pc == low_pc) { |
| 163 | // Merge with the previous entry (extend its range). |
| 164 | variable_locations.back().high_pc = high_pc; |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 165 | } else { |
| 166 | variable_locations.push_back({low_pc, high_pc, reg_lo, reg_hi}); |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | return variable_locations; |
| 171 | } |
| 172 | |
| 173 | // Write table into .debug_loc which describes location of dex register. |
| 174 | // The dex register might be valid only at some points and it might |
| 175 | // move between machine registers and stack. |
| 176 | static void WriteDebugLocEntry(const MethodDebugInfo* method_info, |
David Srbecky | 2ed15b6 | 2016-03-04 11:34:46 +0000 | [diff] [blame] | 177 | const std::vector<DexRegisterMap>& dex_register_maps, |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 178 | uint16_t vreg, |
| 179 | bool is64bitValue, |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 180 | uint64_t compilation_unit_code_address, |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 181 | uint32_t dex_pc_low, |
| 182 | uint32_t dex_pc_high, |
| 183 | InstructionSet isa, |
| 184 | dwarf::DebugInfoEntryWriter<>* debug_info, |
| 185 | std::vector<uint8_t>* debug_loc_buffer, |
| 186 | std::vector<uint8_t>* debug_ranges_buffer) { |
| 187 | using Kind = DexRegisterLocation::Kind; |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 188 | if (method_info->code_info == nullptr || dex_register_maps.empty()) { |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 189 | return; |
| 190 | } |
| 191 | |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 192 | std::vector<VariableLocation> variable_locations = GetVariableLocations( |
| 193 | method_info, |
David Srbecky | 2ed15b6 | 2016-03-04 11:34:46 +0000 | [diff] [blame] | 194 | dex_register_maps, |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 195 | vreg, |
| 196 | is64bitValue, |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 197 | compilation_unit_code_address, |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 198 | dex_pc_low, |
| 199 | dex_pc_high); |
| 200 | |
| 201 | // Write .debug_loc entries. |
David Srbecky | b396c73 | 2016-02-10 14:35:34 +0000 | [diff] [blame] | 202 | dwarf::Writer<> debug_loc(debug_loc_buffer); |
| 203 | const size_t debug_loc_offset = debug_loc.size(); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 204 | const bool is64bit = Is64BitInstructionSet(isa); |
| 205 | std::vector<uint8_t> expr_buffer; |
| 206 | for (const VariableLocation& variable_location : variable_locations) { |
| 207 | // Translate dex register location to DWARF expression. |
| 208 | // Note that 64-bit value might be split to two distinct locations. |
| 209 | // (for example, two 32-bit machine registers, or even stack and register) |
| 210 | dwarf::Expression expr(&expr_buffer); |
| 211 | DexRegisterLocation reg_lo = variable_location.reg_lo; |
| 212 | DexRegisterLocation reg_hi = variable_location.reg_hi; |
| 213 | for (int piece = 0; piece < (is64bitValue ? 2 : 1); piece++) { |
| 214 | DexRegisterLocation reg_loc = (piece == 0 ? reg_lo : reg_hi); |
| 215 | const Kind kind = reg_loc.GetKind(); |
| 216 | const int32_t value = reg_loc.GetValue(); |
| 217 | if (kind == Kind::kInStack) { |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 218 | // The stack offset is relative to SP. Make it relative to CFA. |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 219 | expr.WriteOpFbreg(value - method_info->frame_size_in_bytes); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 220 | if (piece == 0 && reg_hi.GetKind() == Kind::kInStack && |
| 221 | reg_hi.GetValue() == value + 4) { |
| 222 | break; // the high word is correctly implied by the low word. |
| 223 | } |
| 224 | } else if (kind == Kind::kInRegister) { |
| 225 | expr.WriteOpReg(GetDwarfCoreReg(isa, value).num()); |
| 226 | if (piece == 0 && reg_hi.GetKind() == Kind::kInRegisterHigh && |
| 227 | reg_hi.GetValue() == value) { |
| 228 | break; // the high word is correctly implied by the low word. |
| 229 | } |
| 230 | } else if (kind == Kind::kInFpuRegister) { |
| 231 | if ((isa == kArm || isa == kThumb2) && |
| 232 | piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegister && |
| 233 | reg_hi.GetValue() == value + 1 && value % 2 == 0) { |
| 234 | // Translate S register pair to D register (e.g. S4+S5 to D2). |
| 235 | expr.WriteOpReg(Reg::ArmDp(value / 2).num()); |
| 236 | break; |
| 237 | } |
| 238 | expr.WriteOpReg(GetDwarfFpReg(isa, value).num()); |
| 239 | if (piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegisterHigh && |
| 240 | reg_hi.GetValue() == reg_lo.GetValue()) { |
| 241 | break; // the high word is correctly implied by the low word. |
| 242 | } |
| 243 | } else if (kind == Kind::kConstant) { |
| 244 | expr.WriteOpConsts(value); |
| 245 | expr.WriteOpStackValue(); |
| 246 | } else if (kind == Kind::kNone) { |
| 247 | break; |
| 248 | } else { |
| 249 | // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind(). |
| 250 | // kInRegisterHigh and kInFpuRegisterHigh should be handled by |
| 251 | // the special cases above and they should not occur alone. |
David Srbecky | 7dc1178 | 2016-02-25 13:23:56 +0000 | [diff] [blame] | 252 | LOG(ERROR) << "Unexpected register location kind: " << kind; |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 253 | break; |
| 254 | } |
| 255 | if (is64bitValue) { |
| 256 | // Write the marker which is needed by split 64-bit values. |
| 257 | // This code is skipped by the special cases. |
| 258 | expr.WriteOpPiece(4); |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | if (expr.size() > 0) { |
| 263 | if (is64bit) { |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 264 | debug_loc.PushUint64(variable_location.low_pc); |
| 265 | debug_loc.PushUint64(variable_location.high_pc); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 266 | } else { |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 267 | debug_loc.PushUint32(variable_location.low_pc); |
| 268 | debug_loc.PushUint32(variable_location.high_pc); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 269 | } |
| 270 | // Write the expression. |
| 271 | debug_loc.PushUint16(expr.size()); |
| 272 | debug_loc.PushData(expr.data()); |
| 273 | } else { |
| 274 | // Do not generate .debug_loc if the location is not known. |
| 275 | } |
| 276 | } |
| 277 | // Write end-of-list entry. |
| 278 | if (is64bit) { |
| 279 | debug_loc.PushUint64(0); |
| 280 | debug_loc.PushUint64(0); |
| 281 | } else { |
| 282 | debug_loc.PushUint32(0); |
| 283 | debug_loc.PushUint32(0); |
| 284 | } |
| 285 | |
| 286 | // Write .debug_ranges entries. |
| 287 | // 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] | 288 | dwarf::Writer<> debug_ranges(debug_ranges_buffer); |
| 289 | size_t debug_ranges_offset = debug_ranges.size(); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 290 | for (size_t i = 0; i < variable_locations.size(); i++) { |
| 291 | uint32_t low_pc = variable_locations[i].low_pc; |
| 292 | uint32_t high_pc = variable_locations[i].high_pc; |
| 293 | while (i + 1 < variable_locations.size() && variable_locations[i+1].low_pc == high_pc) { |
| 294 | // Merge address range with the next entry. |
| 295 | high_pc = variable_locations[++i].high_pc; |
| 296 | } |
| 297 | if (is64bit) { |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 298 | debug_ranges.PushUint64(low_pc); |
| 299 | debug_ranges.PushUint64(high_pc); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 300 | } else { |
David Srbecky | 197160d | 2016-03-07 17:33:57 +0000 | [diff] [blame] | 301 | debug_ranges.PushUint32(low_pc); |
| 302 | debug_ranges.PushUint32(high_pc); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 303 | } |
| 304 | } |
| 305 | // Write end-of-list entry. |
| 306 | if (is64bit) { |
| 307 | debug_ranges.PushUint64(0); |
| 308 | debug_ranges.PushUint64(0); |
| 309 | } else { |
| 310 | debug_ranges.PushUint32(0); |
| 311 | debug_ranges.PushUint32(0); |
| 312 | } |
David Srbecky | b396c73 | 2016-02-10 14:35:34 +0000 | [diff] [blame] | 313 | |
| 314 | // Simple de-duplication - check whether this entry is same as the last one (or tail of it). |
| 315 | size_t debug_ranges_entry_size = debug_ranges.size() - debug_ranges_offset; |
| 316 | if (debug_ranges_offset >= debug_ranges_entry_size) { |
| 317 | size_t previous_offset = debug_ranges_offset - debug_ranges_entry_size; |
| 318 | if (memcmp(debug_ranges_buffer->data() + previous_offset, |
| 319 | debug_ranges_buffer->data() + debug_ranges_offset, |
| 320 | debug_ranges_entry_size) == 0) { |
| 321 | // Remove what we have just written and use the last entry instead. |
| 322 | debug_ranges_buffer->resize(debug_ranges_offset); |
| 323 | debug_ranges_offset = previous_offset; |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | // Write attributes to .debug_info. |
| 328 | debug_info->WriteSecOffset(dwarf::DW_AT_location, debug_loc_offset); |
| 329 | debug_info->WriteSecOffset(dwarf::DW_AT_start_scope, debug_ranges_offset); |
David Srbecky | c5bfa97 | 2016-02-05 15:49:10 +0000 | [diff] [blame] | 330 | } |
| 331 | |
| 332 | } // namespace debug |
| 333 | } // namespace art |
| 334 | |
| 335 | #endif // ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_ |
| 336 | |