1 /* 2 * Copyright (C) 2015 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_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_ 18 #define ART_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_ 19 20 #include "base/bit_utils.h" 21 #include "debug/dwarf/dwarf_constants.h" 22 #include "debug/dwarf/register.h" 23 #include "debug/dwarf/writer.h" 24 25 namespace art { 26 namespace dwarf { 27 28 // Writer for .debug_frame opcodes (DWARF-3). 29 // See the DWARF specification for the precise meaning of the opcodes. 30 // The writer is very light-weight, however it will do the following for you: 31 // * Choose the most compact encoding of a given opcode. 32 // * Keep track of current state and convert absolute values to deltas. 33 // * Divide by header-defined factors as appropriate. 34 template<typename Vector = std::vector<uint8_t> > 35 class DebugFrameOpCodeWriter : private Writer<Vector> { 36 static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); 37 38 public: 39 // To save space, DWARF divides most offsets by header-defined factors. 40 // They are used in integer divisions, so we make them constants. 41 // We usually subtract from stack base pointer, so making the factor 42 // negative makes the encoded values positive and thus easier to encode. 43 static constexpr int kDataAlignmentFactor = -4; 44 static constexpr int kCodeAlignmentFactor = 1; 45 46 // Explicitely advance the program counter to given location. AdvancePC(int absolute_pc)47 void ALWAYS_INLINE AdvancePC(int absolute_pc) { 48 DCHECK_GE(absolute_pc, current_pc_); 49 if (UNLIKELY(enabled_)) { 50 int delta = FactorCodeOffset(absolute_pc - current_pc_); 51 if (delta != 0) { 52 if (delta <= 0x3F) { 53 this->PushUint8(DW_CFA_advance_loc | delta); 54 } else if (delta <= UINT8_MAX) { 55 this->PushUint8(DW_CFA_advance_loc1); 56 this->PushUint8(delta); 57 } else if (delta <= UINT16_MAX) { 58 this->PushUint8(DW_CFA_advance_loc2); 59 this->PushUint16(delta); 60 } else { 61 this->PushUint8(DW_CFA_advance_loc4); 62 this->PushUint32(delta); 63 } 64 } 65 current_pc_ = absolute_pc; 66 } 67 } 68 69 // Override this method to automatically advance the PC before each opcode. ImplicitlyAdvancePC()70 virtual void ImplicitlyAdvancePC() { } 71 72 // Common alias in assemblers - spill relative to current stack pointer. RelOffset(Reg reg,int offset)73 void ALWAYS_INLINE RelOffset(Reg reg, int offset) { 74 Offset(reg, offset - current_cfa_offset_); 75 } 76 77 // Common alias in assemblers - increase stack frame size. AdjustCFAOffset(int delta)78 void ALWAYS_INLINE AdjustCFAOffset(int delta) { 79 DefCFAOffset(current_cfa_offset_ + delta); 80 } 81 82 // Custom alias - spill many registers based on bitmask. RelOffsetForMany(Reg reg_base,int offset,uint32_t reg_mask,int reg_size)83 void ALWAYS_INLINE RelOffsetForMany(Reg reg_base, int offset, 84 uint32_t reg_mask, int reg_size) { 85 DCHECK(reg_size == 4 || reg_size == 8); 86 if (UNLIKELY(enabled_)) { 87 for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) { 88 // Skip zero bits and go to the set bit. 89 int num_zeros = CTZ(reg_mask); 90 i += num_zeros; 91 reg_mask >>= num_zeros; 92 RelOffset(Reg(reg_base.num() + i), offset); 93 offset += reg_size; 94 } 95 } 96 } 97 98 // Custom alias - unspill many registers based on bitmask. RestoreMany(Reg reg_base,uint32_t reg_mask)99 void ALWAYS_INLINE RestoreMany(Reg reg_base, uint32_t reg_mask) { 100 if (UNLIKELY(enabled_)) { 101 for (int i = 0; reg_mask != 0u; reg_mask >>= 1, i++) { 102 // Skip zero bits and go to the set bit. 103 int num_zeros = CTZ(reg_mask); 104 i += num_zeros; 105 reg_mask >>= num_zeros; 106 Restore(Reg(reg_base.num() + i)); 107 } 108 } 109 } 110 Nop()111 void ALWAYS_INLINE Nop() { 112 if (UNLIKELY(enabled_)) { 113 this->PushUint8(DW_CFA_nop); 114 } 115 } 116 Offset(Reg reg,int offset)117 void ALWAYS_INLINE Offset(Reg reg, int offset) { 118 if (UNLIKELY(enabled_)) { 119 ImplicitlyAdvancePC(); 120 int factored_offset = FactorDataOffset(offset); // May change sign. 121 if (factored_offset >= 0) { 122 if (0 <= reg.num() && reg.num() <= 0x3F) { 123 this->PushUint8(DW_CFA_offset | reg.num()); 124 this->PushUleb128(factored_offset); 125 } else { 126 this->PushUint8(DW_CFA_offset_extended); 127 this->PushUleb128(reg.num()); 128 this->PushUleb128(factored_offset); 129 } 130 } else { 131 uses_dwarf3_features_ = true; 132 this->PushUint8(DW_CFA_offset_extended_sf); 133 this->PushUleb128(reg.num()); 134 this->PushSleb128(factored_offset); 135 } 136 } 137 } 138 Restore(Reg reg)139 void ALWAYS_INLINE Restore(Reg reg) { 140 if (UNLIKELY(enabled_)) { 141 ImplicitlyAdvancePC(); 142 if (0 <= reg.num() && reg.num() <= 0x3F) { 143 this->PushUint8(DW_CFA_restore | reg.num()); 144 } else { 145 this->PushUint8(DW_CFA_restore_extended); 146 this->PushUleb128(reg.num()); 147 } 148 } 149 } 150 Undefined(Reg reg)151 void ALWAYS_INLINE Undefined(Reg reg) { 152 if (UNLIKELY(enabled_)) { 153 ImplicitlyAdvancePC(); 154 this->PushUint8(DW_CFA_undefined); 155 this->PushUleb128(reg.num()); 156 } 157 } 158 SameValue(Reg reg)159 void ALWAYS_INLINE SameValue(Reg reg) { 160 if (UNLIKELY(enabled_)) { 161 ImplicitlyAdvancePC(); 162 this->PushUint8(DW_CFA_same_value); 163 this->PushUleb128(reg.num()); 164 } 165 } 166 167 // The previous value of "reg" is stored in register "new_reg". Register(Reg reg,Reg new_reg)168 void ALWAYS_INLINE Register(Reg reg, Reg new_reg) { 169 if (UNLIKELY(enabled_)) { 170 ImplicitlyAdvancePC(); 171 this->PushUint8(DW_CFA_register); 172 this->PushUleb128(reg.num()); 173 this->PushUleb128(new_reg.num()); 174 } 175 } 176 RememberState()177 void ALWAYS_INLINE RememberState() { 178 if (UNLIKELY(enabled_)) { 179 ImplicitlyAdvancePC(); 180 this->PushUint8(DW_CFA_remember_state); 181 } 182 } 183 RestoreState()184 void ALWAYS_INLINE RestoreState() { 185 if (UNLIKELY(enabled_)) { 186 ImplicitlyAdvancePC(); 187 this->PushUint8(DW_CFA_restore_state); 188 } 189 } 190 DefCFA(Reg reg,int offset)191 void ALWAYS_INLINE DefCFA(Reg reg, int offset) { 192 if (UNLIKELY(enabled_)) { 193 ImplicitlyAdvancePC(); 194 if (offset >= 0) { 195 this->PushUint8(DW_CFA_def_cfa); 196 this->PushUleb128(reg.num()); 197 this->PushUleb128(offset); // Non-factored. 198 } else { 199 uses_dwarf3_features_ = true; 200 this->PushUint8(DW_CFA_def_cfa_sf); 201 this->PushUleb128(reg.num()); 202 this->PushSleb128(FactorDataOffset(offset)); 203 } 204 } 205 current_cfa_offset_ = offset; 206 } 207 DefCFARegister(Reg reg)208 void ALWAYS_INLINE DefCFARegister(Reg reg) { 209 if (UNLIKELY(enabled_)) { 210 ImplicitlyAdvancePC(); 211 this->PushUint8(DW_CFA_def_cfa_register); 212 this->PushUleb128(reg.num()); 213 } 214 } 215 DefCFAOffset(int offset)216 void ALWAYS_INLINE DefCFAOffset(int offset) { 217 if (UNLIKELY(enabled_)) { 218 if (current_cfa_offset_ != offset) { 219 ImplicitlyAdvancePC(); 220 if (offset >= 0) { 221 this->PushUint8(DW_CFA_def_cfa_offset); 222 this->PushUleb128(offset); // Non-factored. 223 } else { 224 uses_dwarf3_features_ = true; 225 this->PushUint8(DW_CFA_def_cfa_offset_sf); 226 this->PushSleb128(FactorDataOffset(offset)); 227 } 228 } 229 } 230 // Uncoditional so that the user can still get and check the value. 231 current_cfa_offset_ = offset; 232 } 233 ValOffset(Reg reg,int offset)234 void ALWAYS_INLINE ValOffset(Reg reg, int offset) { 235 if (UNLIKELY(enabled_)) { 236 ImplicitlyAdvancePC(); 237 uses_dwarf3_features_ = true; 238 int factored_offset = FactorDataOffset(offset); // May change sign. 239 if (factored_offset >= 0) { 240 this->PushUint8(DW_CFA_val_offset); 241 this->PushUleb128(reg.num()); 242 this->PushUleb128(factored_offset); 243 } else { 244 this->PushUint8(DW_CFA_val_offset_sf); 245 this->PushUleb128(reg.num()); 246 this->PushSleb128(factored_offset); 247 } 248 } 249 } 250 DefCFAExpression(uint8_t * expr,int expr_size)251 void ALWAYS_INLINE DefCFAExpression(uint8_t* expr, int expr_size) { 252 if (UNLIKELY(enabled_)) { 253 ImplicitlyAdvancePC(); 254 uses_dwarf3_features_ = true; 255 this->PushUint8(DW_CFA_def_cfa_expression); 256 this->PushUleb128(expr_size); 257 this->PushData(expr, expr_size); 258 } 259 } 260 Expression(Reg reg,uint8_t * expr,int expr_size)261 void ALWAYS_INLINE Expression(Reg reg, uint8_t* expr, int expr_size) { 262 if (UNLIKELY(enabled_)) { 263 ImplicitlyAdvancePC(); 264 uses_dwarf3_features_ = true; 265 this->PushUint8(DW_CFA_expression); 266 this->PushUleb128(reg.num()); 267 this->PushUleb128(expr_size); 268 this->PushData(expr, expr_size); 269 } 270 } 271 ValExpression(Reg reg,uint8_t * expr,int expr_size)272 void ALWAYS_INLINE ValExpression(Reg reg, uint8_t* expr, int expr_size) { 273 if (UNLIKELY(enabled_)) { 274 ImplicitlyAdvancePC(); 275 uses_dwarf3_features_ = true; 276 this->PushUint8(DW_CFA_val_expression); 277 this->PushUleb128(reg.num()); 278 this->PushUleb128(expr_size); 279 this->PushData(expr, expr_size); 280 } 281 } 282 IsEnabled()283 bool IsEnabled() const { return enabled_; } 284 SetEnabled(bool value)285 void SetEnabled(bool value) { 286 enabled_ = value; 287 if (enabled_ && opcodes_.capacity() == 0u) { 288 opcodes_.reserve(kDefaultCapacity); 289 } 290 } 291 GetCurrentPC()292 int GetCurrentPC() const { return current_pc_; } 293 GetCurrentCFAOffset()294 int GetCurrentCFAOffset() const { return current_cfa_offset_; } 295 SetCurrentCFAOffset(int offset)296 void SetCurrentCFAOffset(int offset) { current_cfa_offset_ = offset; } 297 298 using Writer<Vector>::data; 299 300 explicit DebugFrameOpCodeWriter(bool enabled = true, 301 const typename Vector::allocator_type& alloc = 302 typename Vector::allocator_type()) 303 : Writer<Vector>(&opcodes_), 304 enabled_(false), 305 opcodes_(alloc), 306 current_cfa_offset_(0), 307 current_pc_(0), 308 uses_dwarf3_features_(false) { 309 SetEnabled(enabled); 310 } 311 ~DebugFrameOpCodeWriter()312 virtual ~DebugFrameOpCodeWriter() { } 313 314 protected: 315 // Best guess based on couple of observed outputs. 316 static constexpr size_t kDefaultCapacity = 32u; 317 FactorDataOffset(int offset)318 int FactorDataOffset(int offset) const { 319 DCHECK_EQ(offset % kDataAlignmentFactor, 0); 320 return offset / kDataAlignmentFactor; 321 } 322 FactorCodeOffset(int offset)323 int FactorCodeOffset(int offset) const { 324 DCHECK_EQ(offset % kCodeAlignmentFactor, 0); 325 return offset / kCodeAlignmentFactor; 326 } 327 328 bool enabled_; // If disabled all writes are no-ops. 329 Vector opcodes_; 330 int current_cfa_offset_; 331 int current_pc_; 332 bool uses_dwarf3_features_; 333 334 private: 335 DISALLOW_COPY_AND_ASSIGN(DebugFrameOpCodeWriter); 336 }; 337 338 } // namespace dwarf 339 } // namespace art 340 341 #endif // ART_COMPILER_DEBUG_DWARF_DEBUG_FRAME_OPCODE_WRITER_H_ 342