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_LINE_OPCODE_WRITER_H_ 18#define ART_COMPILER_DEBUG_DWARF_DEBUG_LINE_OPCODE_WRITER_H_ 19 20#include <cstdint> 21 22#include "debug/dwarf/dwarf_constants.h" 23#include "debug/dwarf/writer.h" 24 25namespace art { 26namespace dwarf { 27 28// Writer for the .debug_line opcodes (DWARF-3). 29// The writer is very light-weight, however it will do the following for you: 30// * Choose the most compact encoding of a given opcode. 31// * Keep track of current state and convert absolute values to deltas. 32// * Divide by header-defined factors as appropriate. 33template<typename Vector = std::vector<uint8_t>> 34class DebugLineOpCodeWriter FINAL : private Writer<Vector> { 35 static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); 36 37 public: 38 static constexpr int kOpcodeBase = 13; 39 static constexpr bool kDefaultIsStmt = false; 40 static constexpr int kLineBase = -5; 41 static constexpr int kLineRange = 14; 42 43 void AddRow() { 44 this->PushUint8(DW_LNS_copy); 45 } 46 47 void AdvancePC(uint64_t absolute_address) { 48 DCHECK_NE(current_address_, 0u); // Use SetAddress for the first advance. 49 DCHECK_GE(absolute_address, current_address_); 50 if (absolute_address != current_address_) { 51 uint64_t delta = FactorCodeOffset(absolute_address - current_address_); 52 if (delta <= INT32_MAX) { 53 this->PushUint8(DW_LNS_advance_pc); 54 this->PushUleb128(static_cast<int>(delta)); 55 current_address_ = absolute_address; 56 } else { 57 SetAddress(absolute_address); 58 } 59 } 60 } 61 62 void AdvanceLine(int absolute_line) { 63 int delta = absolute_line - current_line_; 64 if (delta != 0) { 65 this->PushUint8(DW_LNS_advance_line); 66 this->PushSleb128(delta); 67 current_line_ = absolute_line; 68 } 69 } 70 71 void SetFile(int file) { 72 if (current_file_ != file) { 73 this->PushUint8(DW_LNS_set_file); 74 this->PushUleb128(file); 75 current_file_ = file; 76 } 77 } 78 79 void SetColumn(int column) { 80 this->PushUint8(DW_LNS_set_column); 81 this->PushUleb128(column); 82 } 83 84 void SetIsStmt(bool is_stmt) { 85 if (is_stmt_ != is_stmt) { 86 this->PushUint8(DW_LNS_negate_stmt); 87 is_stmt_ = is_stmt; 88 } 89 } 90 91 void SetBasicBlock() { 92 this->PushUint8(DW_LNS_set_basic_block); 93 } 94 95 void SetPrologueEnd() { 96 uses_dwarf3_features_ = true; 97 this->PushUint8(DW_LNS_set_prologue_end); 98 } 99 100 void SetEpilogueBegin() { 101 uses_dwarf3_features_ = true; 102 this->PushUint8(DW_LNS_set_epilogue_begin); 103 } 104 105 void SetISA(int isa) { 106 uses_dwarf3_features_ = true; 107 this->PushUint8(DW_LNS_set_isa); 108 this->PushUleb128(isa); 109 } 110 111 void EndSequence() { 112 this->PushUint8(0); 113 this->PushUleb128(1); 114 this->PushUint8(DW_LNE_end_sequence); 115 current_address_ = 0; 116 current_file_ = 1; 117 current_line_ = 1; 118 is_stmt_ = kDefaultIsStmt; 119 } 120 121 // Uncoditionally set address using the long encoding. 122 // This gives the linker opportunity to relocate the address. 123 void SetAddress(uint64_t absolute_address) { 124 DCHECK_GE(absolute_address, current_address_); 125 FactorCodeOffset(absolute_address); // Check if it is factorable. 126 this->PushUint8(0); 127 if (use_64bit_address_) { 128 this->PushUleb128(1 + 8); 129 this->PushUint8(DW_LNE_set_address); 130 patch_locations_.push_back(this->data()->size()); 131 this->PushUint64(absolute_address); 132 } else { 133 this->PushUleb128(1 + 4); 134 this->PushUint8(DW_LNE_set_address); 135 patch_locations_.push_back(this->data()->size()); 136 this->PushUint32(absolute_address); 137 } 138 current_address_ = absolute_address; 139 } 140 141 void DefineFile(const char* filename, 142 int directory_index, 143 int modification_time, 144 int file_size) { 145 int size = 1 + 146 strlen(filename) + 1 + 147 UnsignedLeb128Size(directory_index) + 148 UnsignedLeb128Size(modification_time) + 149 UnsignedLeb128Size(file_size); 150 this->PushUint8(0); 151 this->PushUleb128(size); 152 size_t start = data()->size(); 153 this->PushUint8(DW_LNE_define_file); 154 this->PushString(filename); 155 this->PushUleb128(directory_index); 156 this->PushUleb128(modification_time); 157 this->PushUleb128(file_size); 158 DCHECK_EQ(start + size, data()->size()); 159 } 160 161 // Compact address and line opcode. 162 void AddRow(uint64_t absolute_address, int absolute_line) { 163 DCHECK_GE(absolute_address, current_address_); 164 165 // If the address is definitely too far, use the long encoding. 166 uint64_t delta_address = FactorCodeOffset(absolute_address - current_address_); 167 if (delta_address > UINT8_MAX) { 168 AdvancePC(absolute_address); 169 delta_address = 0; 170 } 171 172 // If the line is definitely too far, use the long encoding. 173 int delta_line = absolute_line - current_line_; 174 if (!(kLineBase <= delta_line && delta_line < kLineBase + kLineRange)) { 175 AdvanceLine(absolute_line); 176 delta_line = 0; 177 } 178 179 // Both address and line should be reasonable now. Use the short encoding. 180 int opcode = kOpcodeBase + (delta_line - kLineBase) + 181 (static_cast<int>(delta_address) * kLineRange); 182 if (opcode > UINT8_MAX) { 183 // If the address is still too far, try to increment it by const amount. 184 int const_advance = (0xFF - kOpcodeBase) / kLineRange; 185 opcode -= (kLineRange * const_advance); 186 if (opcode <= UINT8_MAX) { 187 this->PushUint8(DW_LNS_const_add_pc); 188 } else { 189 // Give up and use long encoding for address. 190 AdvancePC(absolute_address); 191 // Still use the opcode to do line advance and copy. 192 opcode = kOpcodeBase + (delta_line - kLineBase); 193 } 194 } 195 DCHECK(kOpcodeBase <= opcode && opcode <= 0xFF); 196 this->PushUint8(opcode); // Special opcode. 197 current_line_ = absolute_line; 198 current_address_ = absolute_address; 199 } 200 201 int GetCodeFactorBits() const { 202 return code_factor_bits_; 203 } 204 205 uint64_t CurrentAddress() const { 206 return current_address_; 207 } 208 209 int CurrentFile() const { 210 return current_file_; 211 } 212 213 int CurrentLine() const { 214 return current_line_; 215 } 216 217 const std::vector<uintptr_t>& GetPatchLocations() const { 218 return patch_locations_; 219 } 220 221 using Writer<Vector>::data; 222 223 DebugLineOpCodeWriter(bool use64bitAddress, 224 int codeFactorBits, 225 const typename Vector::allocator_type& alloc = 226 typename Vector::allocator_type()) 227 : Writer<Vector>(&opcodes_), 228 opcodes_(alloc), 229 uses_dwarf3_features_(false), 230 use_64bit_address_(use64bitAddress), 231 code_factor_bits_(codeFactorBits), 232 current_address_(0), 233 current_file_(1), 234 current_line_(1), 235 is_stmt_(kDefaultIsStmt) { 236 } 237 238 private: 239 uint64_t FactorCodeOffset(uint64_t offset) const { 240 DCHECK_GE(code_factor_bits_, 0); 241 DCHECK_EQ((offset >> code_factor_bits_) << code_factor_bits_, offset); 242 return offset >> code_factor_bits_; 243 } 244 245 Vector opcodes_; 246 bool uses_dwarf3_features_; 247 bool use_64bit_address_; 248 int code_factor_bits_; 249 uint64_t current_address_; 250 int current_file_; 251 int current_line_; 252 bool is_stmt_; 253 std::vector<uintptr_t> patch_locations_; 254 255 DISALLOW_COPY_AND_ASSIGN(DebugLineOpCodeWriter); 256}; 257 258} // namespace dwarf 259} // namespace art 260 261#endif // ART_COMPILER_DEBUG_DWARF_DEBUG_LINE_OPCODE_WRITER_H_ 262