blob: e522e130dc8e2a5474674fe00434aa2160e34e74 [file] [log] [blame]
David Srbeckyc5bfa972016-02-05 15:49:10 +00001/*
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 Srbeckyb396c732016-02-10 14:35:34 +000020#include <cstring>
David Srbeckyc5bfa972016-02-05 15:49:10 +000021#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
30namespace art {
31namespace debug {
32using Reg = dwarf::Reg;
33
34static 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
55static 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
76struct VariableLocation {
77 uint32_t low_pc;
78 uint32_t high_pc;
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.
86std::vector<VariableLocation> GetVariableLocations(const MethodDebugInfo* method_info,
87 uint16_t vreg,
88 bool is64bitValue,
89 uint32_t dex_pc_low,
90 uint32_t dex_pc_high) {
91 std::vector<VariableLocation> variable_locations;
92
93 // Get stack maps sorted by pc (they might not be sorted internally).
94 const CodeInfo code_info(method_info->compiled_method->GetVmapTable().data());
95 const StackMapEncoding encoding = code_info.ExtractEncoding();
96 std::map<uint32_t, StackMap> stack_maps;
97 for (uint32_t s = 0; s < code_info.GetNumberOfStackMaps(); s++) {
98 StackMap stack_map = code_info.GetStackMapAt(s, encoding);
99 DCHECK(stack_map.IsValid());
100 const uint32_t low_pc = method_info->low_pc + stack_map.GetNativePcOffset(encoding);
101 DCHECK_LE(low_pc, method_info->high_pc);
102 stack_maps.emplace(low_pc, stack_map);
103 }
104
105 // Create entries for the requested register based on stack map data.
106 for (auto it = stack_maps.begin(); it != stack_maps.end(); it++) {
107 const StackMap& stack_map = it->second;
108 const uint32_t low_pc = it->first;
109 auto next_it = it;
110 next_it++;
111 const uint32_t high_pc = next_it != stack_maps.end() ? next_it->first
112 : method_info->high_pc;
113 DCHECK_LE(low_pc, high_pc);
114 if (low_pc == high_pc) {
115 continue; // Ignore if the address range is empty.
116 }
117
118 // Check that the stack map is in the requested range.
119 uint32_t dex_pc = stack_map.GetDexPc(encoding);
120 if (!(dex_pc_low <= dex_pc && dex_pc < dex_pc_high)) {
121 continue;
122 }
123
124 // Find the location of the dex register.
125 DexRegisterLocation reg_lo = DexRegisterLocation::None();
126 DexRegisterLocation reg_hi = DexRegisterLocation::None();
127 if (stack_map.HasDexRegisterMap(encoding)) {
128 DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(
129 stack_map, encoding, method_info->code_item->registers_size_);
130 reg_lo = dex_register_map.GetDexRegisterLocation(
131 vreg, method_info->code_item->registers_size_, code_info, encoding);
132 if (is64bitValue) {
133 reg_hi = dex_register_map.GetDexRegisterLocation(
134 vreg + 1, method_info->code_item->registers_size_, code_info, encoding);
135 }
136 }
137
138 // Add location entry for this address range.
139 if (!variable_locations.empty() &&
140 variable_locations.back().reg_lo == reg_lo &&
141 variable_locations.back().reg_hi == reg_hi &&
142 variable_locations.back().high_pc == low_pc) {
143 // Merge with the previous entry (extend its range).
144 variable_locations.back().high_pc = high_pc;
145 } else {
146 variable_locations.push_back({low_pc, high_pc, reg_lo, reg_hi});
147 }
148 }
149
150 return variable_locations;
151}
152
153// Write table into .debug_loc which describes location of dex register.
154// The dex register might be valid only at some points and it might
155// move between machine registers and stack.
156static void WriteDebugLocEntry(const MethodDebugInfo* method_info,
157 uint16_t vreg,
158 bool is64bitValue,
159 uint32_t compilation_unit_low_pc,
160 uint32_t dex_pc_low,
161 uint32_t dex_pc_high,
162 InstructionSet isa,
163 dwarf::DebugInfoEntryWriter<>* debug_info,
164 std::vector<uint8_t>* debug_loc_buffer,
165 std::vector<uint8_t>* debug_ranges_buffer) {
166 using Kind = DexRegisterLocation::Kind;
167 if (!method_info->IsFromOptimizingCompiler()) {
168 return;
169 }
170
David Srbeckyc5bfa972016-02-05 15:49:10 +0000171 std::vector<VariableLocation> variable_locations = GetVariableLocations(
172 method_info,
173 vreg,
174 is64bitValue,
175 dex_pc_low,
176 dex_pc_high);
177
178 // Write .debug_loc entries.
David Srbeckyb396c732016-02-10 14:35:34 +0000179 dwarf::Writer<> debug_loc(debug_loc_buffer);
180 const size_t debug_loc_offset = debug_loc.size();
David Srbeckyc5bfa972016-02-05 15:49:10 +0000181 const bool is64bit = Is64BitInstructionSet(isa);
182 std::vector<uint8_t> expr_buffer;
183 for (const VariableLocation& variable_location : variable_locations) {
184 // Translate dex register location to DWARF expression.
185 // Note that 64-bit value might be split to two distinct locations.
186 // (for example, two 32-bit machine registers, or even stack and register)
187 dwarf::Expression expr(&expr_buffer);
188 DexRegisterLocation reg_lo = variable_location.reg_lo;
189 DexRegisterLocation reg_hi = variable_location.reg_hi;
190 for (int piece = 0; piece < (is64bitValue ? 2 : 1); piece++) {
191 DexRegisterLocation reg_loc = (piece == 0 ? reg_lo : reg_hi);
192 const Kind kind = reg_loc.GetKind();
193 const int32_t value = reg_loc.GetValue();
194 if (kind == Kind::kInStack) {
195 const size_t frame_size = method_info->compiled_method->GetFrameSizeInBytes();
196 // The stack offset is relative to SP. Make it relative to CFA.
197 expr.WriteOpFbreg(value - frame_size);
198 if (piece == 0 && reg_hi.GetKind() == Kind::kInStack &&
199 reg_hi.GetValue() == value + 4) {
200 break; // the high word is correctly implied by the low word.
201 }
202 } else if (kind == Kind::kInRegister) {
203 expr.WriteOpReg(GetDwarfCoreReg(isa, value).num());
204 if (piece == 0 && reg_hi.GetKind() == Kind::kInRegisterHigh &&
205 reg_hi.GetValue() == value) {
206 break; // the high word is correctly implied by the low word.
207 }
208 } else if (kind == Kind::kInFpuRegister) {
209 if ((isa == kArm || isa == kThumb2) &&
210 piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegister &&
211 reg_hi.GetValue() == value + 1 && value % 2 == 0) {
212 // Translate S register pair to D register (e.g. S4+S5 to D2).
213 expr.WriteOpReg(Reg::ArmDp(value / 2).num());
214 break;
215 }
216 expr.WriteOpReg(GetDwarfFpReg(isa, value).num());
217 if (piece == 0 && reg_hi.GetKind() == Kind::kInFpuRegisterHigh &&
218 reg_hi.GetValue() == reg_lo.GetValue()) {
219 break; // the high word is correctly implied by the low word.
220 }
221 } else if (kind == Kind::kConstant) {
222 expr.WriteOpConsts(value);
223 expr.WriteOpStackValue();
224 } else if (kind == Kind::kNone) {
225 break;
226 } else {
227 // kInStackLargeOffset and kConstantLargeValue are hidden by GetKind().
228 // kInRegisterHigh and kInFpuRegisterHigh should be handled by
229 // the special cases above and they should not occur alone.
230 LOG(ERROR) << "Unexpected register location kind: "
231 << DexRegisterLocation::PrettyDescriptor(kind);
232 break;
233 }
234 if (is64bitValue) {
235 // Write the marker which is needed by split 64-bit values.
236 // This code is skipped by the special cases.
237 expr.WriteOpPiece(4);
238 }
239 }
240
241 if (expr.size() > 0) {
242 if (is64bit) {
243 debug_loc.PushUint64(variable_location.low_pc - compilation_unit_low_pc);
244 debug_loc.PushUint64(variable_location.high_pc - compilation_unit_low_pc);
245 } else {
246 debug_loc.PushUint32(variable_location.low_pc - compilation_unit_low_pc);
247 debug_loc.PushUint32(variable_location.high_pc - compilation_unit_low_pc);
248 }
249 // Write the expression.
250 debug_loc.PushUint16(expr.size());
251 debug_loc.PushData(expr.data());
252 } else {
253 // Do not generate .debug_loc if the location is not known.
254 }
255 }
256 // Write end-of-list entry.
257 if (is64bit) {
258 debug_loc.PushUint64(0);
259 debug_loc.PushUint64(0);
260 } else {
261 debug_loc.PushUint32(0);
262 debug_loc.PushUint32(0);
263 }
264
265 // Write .debug_ranges entries.
266 // This includes ranges where the variable is in scope but the location is not known.
David Srbeckyb396c732016-02-10 14:35:34 +0000267 dwarf::Writer<> debug_ranges(debug_ranges_buffer);
268 size_t debug_ranges_offset = debug_ranges.size();
David Srbeckyc5bfa972016-02-05 15:49:10 +0000269 for (size_t i = 0; i < variable_locations.size(); i++) {
270 uint32_t low_pc = variable_locations[i].low_pc;
271 uint32_t high_pc = variable_locations[i].high_pc;
272 while (i + 1 < variable_locations.size() && variable_locations[i+1].low_pc == high_pc) {
273 // Merge address range with the next entry.
274 high_pc = variable_locations[++i].high_pc;
275 }
276 if (is64bit) {
277 debug_ranges.PushUint64(low_pc - compilation_unit_low_pc);
278 debug_ranges.PushUint64(high_pc - compilation_unit_low_pc);
279 } else {
280 debug_ranges.PushUint32(low_pc - compilation_unit_low_pc);
281 debug_ranges.PushUint32(high_pc - compilation_unit_low_pc);
282 }
283 }
284 // Write end-of-list entry.
285 if (is64bit) {
286 debug_ranges.PushUint64(0);
287 debug_ranges.PushUint64(0);
288 } else {
289 debug_ranges.PushUint32(0);
290 debug_ranges.PushUint32(0);
291 }
David Srbeckyb396c732016-02-10 14:35:34 +0000292
293 // Simple de-duplication - check whether this entry is same as the last one (or tail of it).
294 size_t debug_ranges_entry_size = debug_ranges.size() - debug_ranges_offset;
295 if (debug_ranges_offset >= debug_ranges_entry_size) {
296 size_t previous_offset = debug_ranges_offset - debug_ranges_entry_size;
297 if (memcmp(debug_ranges_buffer->data() + previous_offset,
298 debug_ranges_buffer->data() + debug_ranges_offset,
299 debug_ranges_entry_size) == 0) {
300 // Remove what we have just written and use the last entry instead.
301 debug_ranges_buffer->resize(debug_ranges_offset);
302 debug_ranges_offset = previous_offset;
303 }
304 }
305
306 // Write attributes to .debug_info.
307 debug_info->WriteSecOffset(dwarf::DW_AT_location, debug_loc_offset);
308 debug_info->WriteSecOffset(dwarf::DW_AT_start_scope, debug_ranges_offset);
David Srbeckyc5bfa972016-02-05 15:49:10 +0000309}
310
311} // namespace debug
312} // namespace art
313
314#endif // ART_COMPILER_DEBUG_ELF_DEBUG_LOC_WRITER_H_
315