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