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