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_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.
     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.
     70   virtual void ImplicitlyAdvancePC() { }
     71 
     72   // Common alias in assemblers - spill relative to current stack pointer.
     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.
     78   void ALWAYS_INLINE AdjustCFAOffset(int delta) {
     79     DefCFAOffset(current_cfa_offset_ + delta);
     80   }
     81 
     82   // Custom alias - spill many registers based on bitmask.
     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.
     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 
    111   void ALWAYS_INLINE Nop() {
    112     if (UNLIKELY(enabled_)) {
    113       this->PushUint8(DW_CFA_nop);
    114     }
    115   }
    116 
    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 
    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 
    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 
    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".
    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 
    177   void ALWAYS_INLINE RememberState() {
    178     if (UNLIKELY(enabled_)) {
    179       ImplicitlyAdvancePC();
    180       this->PushUint8(DW_CFA_remember_state);
    181     }
    182   }
    183 
    184   void ALWAYS_INLINE RestoreState() {
    185     if (UNLIKELY(enabled_)) {
    186       ImplicitlyAdvancePC();
    187       this->PushUint8(DW_CFA_restore_state);
    188     }
    189   }
    190 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    283   bool IsEnabled() const { return enabled_; }
    284 
    285   void SetEnabled(bool value) {
    286     enabled_ = value;
    287     if (enabled_ && opcodes_.capacity() == 0u) {
    288       opcodes_.reserve(kDefaultCapacity);
    289     }
    290   }
    291 
    292   int GetCurrentPC() const { return current_pc_; }
    293 
    294   int GetCurrentCFAOffset() const { return current_cfa_offset_; }
    295 
    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 
    312   virtual ~DebugFrameOpCodeWriter() { }
    313 
    314  protected:
    315   // Best guess based on couple of observed outputs.
    316   static constexpr size_t kDefaultCapacity = 32u;
    317 
    318   int FactorDataOffset(int offset) const {
    319     DCHECK_EQ(offset % kDataAlignmentFactor, 0);
    320     return offset / kDataAlignmentFactor;
    321   }
    322 
    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