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