Home | History | Annotate | Download | only in src
      1 // Copyright 2011 the V8 project authors. All rights reserved.
      2 // Redistribution and use in source and binary forms, with or without
      3 // modification, are permitted provided that the following conditions are
      4 // met:
      5 //
      6 //     * Redistributions of source code must retain the above copyright
      7 //       notice, this list of conditions and the following disclaimer.
      8 //     * Redistributions in binary form must reproduce the above
      9 //       copyright notice, this list of conditions and the following
     10 //       disclaimer in the documentation and/or other materials provided
     11 //       with the distribution.
     12 //     * Neither the name of Google Inc. nor the names of its
     13 //       contributors may be used to endorse or promote products derived
     14 //       from this software without specific prior written permission.
     15 //
     16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27 
     28 #include "v8.h"
     29 
     30 #include "code-stubs.h"
     31 #include "codegen.h"
     32 #include "debug.h"
     33 #include "deoptimizer.h"
     34 #include "disasm.h"
     35 #include "disassembler.h"
     36 #include "macro-assembler.h"
     37 #include "serialize.h"
     38 #include "string-stream.h"
     39 
     40 namespace v8 {
     41 namespace internal {
     42 
     43 #ifdef ENABLE_DISASSEMBLER
     44 
     45 void Disassembler::Dump(FILE* f, byte* begin, byte* end) {
     46   for (byte* pc = begin; pc < end; pc++) {
     47     if (f == NULL) {
     48       PrintF("%" V8PRIxPTR "  %4" V8PRIdPTR "  %02x\n",
     49              reinterpret_cast<intptr_t>(pc),
     50              pc - begin,
     51              *pc);
     52     } else {
     53       fprintf(f, "%" V8PRIxPTR "  %4" V8PRIdPTR "  %02x\n",
     54               reinterpret_cast<uintptr_t>(pc), pc - begin, *pc);
     55     }
     56   }
     57 }
     58 
     59 
     60 class V8NameConverter: public disasm::NameConverter {
     61  public:
     62   explicit V8NameConverter(Code* code) : code_(code) {}
     63   virtual const char* NameOfAddress(byte* pc) const;
     64   virtual const char* NameInCode(byte* addr) const;
     65   Code* code() const { return code_; }
     66  private:
     67   Code* code_;
     68 
     69   EmbeddedVector<char, 128> v8_buffer_;
     70 };
     71 
     72 
     73 const char* V8NameConverter::NameOfAddress(byte* pc) const {
     74   const char* name = Isolate::Current()->builtins()->Lookup(pc);
     75   if (name != NULL) {
     76     OS::SNPrintF(v8_buffer_, "%s  (%p)", name, pc);
     77     return v8_buffer_.start();
     78   }
     79 
     80   if (code_ != NULL) {
     81     int offs = static_cast<int>(pc - code_->instruction_start());
     82     // print as code offset, if it seems reasonable
     83     if (0 <= offs && offs < code_->instruction_size()) {
     84       OS::SNPrintF(v8_buffer_, "%d  (%p)", offs, pc);
     85       return v8_buffer_.start();
     86     }
     87   }
     88 
     89   return disasm::NameConverter::NameOfAddress(pc);
     90 }
     91 
     92 
     93 const char* V8NameConverter::NameInCode(byte* addr) const {
     94   // The V8NameConverter is used for well known code, so we can "safely"
     95   // dereference pointers in generated code.
     96   return (code_ != NULL) ? reinterpret_cast<const char*>(addr) : "";
     97 }
     98 
     99 
    100 static void DumpBuffer(FILE* f, char* buff) {
    101   if (f == NULL) {
    102     PrintF("%s", buff);
    103   } else {
    104     fprintf(f, "%s", buff);
    105   }
    106 }
    107 
    108 static const int kOutBufferSize = 2048 + String::kMaxShortPrintLength;
    109 static const int kRelocInfoPosition = 57;
    110 
    111 static int DecodeIt(FILE* f,
    112                     const V8NameConverter& converter,
    113                     byte* begin,
    114                     byte* end) {
    115   NoHandleAllocation ha;
    116   AssertNoAllocation no_alloc;
    117   ExternalReferenceEncoder ref_encoder;
    118   Heap* heap = HEAP;
    119 
    120   v8::internal::EmbeddedVector<char, 128> decode_buffer;
    121   v8::internal::EmbeddedVector<char, kOutBufferSize> out_buffer;
    122   byte* pc = begin;
    123   disasm::Disassembler d(converter);
    124   RelocIterator* it = NULL;
    125   if (converter.code() != NULL) {
    126     it = new RelocIterator(converter.code());
    127   } else {
    128     // No relocation information when printing code stubs.
    129   }
    130   int constants = -1;  // no constants being decoded at the start
    131 
    132   while (pc < end) {
    133     // First decode instruction so that we know its length.
    134     byte* prev_pc = pc;
    135     if (constants > 0) {
    136       OS::SNPrintF(decode_buffer,
    137                    "%08x       constant",
    138                    *reinterpret_cast<int32_t*>(pc));
    139       constants--;
    140       pc += 4;
    141     } else {
    142       int num_const = d.ConstantPoolSizeAt(pc);
    143       if (num_const >= 0) {
    144         OS::SNPrintF(decode_buffer,
    145                      "%08x       constant pool begin",
    146                      *reinterpret_cast<int32_t*>(pc));
    147         constants = num_const;
    148         pc += 4;
    149       } else if (it != NULL && !it->done() && it->rinfo()->pc() == pc &&
    150           it->rinfo()->rmode() == RelocInfo::INTERNAL_REFERENCE) {
    151         // raw pointer embedded in code stream, e.g., jump table
    152         byte* ptr = *reinterpret_cast<byte**>(pc);
    153         OS::SNPrintF(decode_buffer,
    154                      "%08" V8PRIxPTR "      jump table entry %4" V8PRIdPTR,
    155                      ptr,
    156                      ptr - begin);
    157         pc += 4;
    158       } else {
    159         decode_buffer[0] = '\0';
    160         pc += d.InstructionDecode(decode_buffer, pc);
    161       }
    162     }
    163 
    164     // Collect RelocInfo for this instruction (prev_pc .. pc-1)
    165     List<const char*> comments(4);
    166     List<byte*> pcs(1);
    167     List<RelocInfo::Mode> rmodes(1);
    168     List<intptr_t> datas(1);
    169     if (it != NULL) {
    170       while (!it->done() && it->rinfo()->pc() < pc) {
    171         if (RelocInfo::IsComment(it->rinfo()->rmode())) {
    172           // For comments just collect the text.
    173           comments.Add(reinterpret_cast<const char*>(it->rinfo()->data()));
    174         } else {
    175           // For other reloc info collect all data.
    176           pcs.Add(it->rinfo()->pc());
    177           rmodes.Add(it->rinfo()->rmode());
    178           datas.Add(it->rinfo()->data());
    179         }
    180         it->next();
    181       }
    182     }
    183 
    184     StringBuilder out(out_buffer.start(), out_buffer.length());
    185 
    186     // Comments.
    187     for (int i = 0; i < comments.length(); i++) {
    188       out.AddFormatted("                  %s\n", comments[i]);
    189     }
    190 
    191     // Write out comments, resets outp so that we can format the next line.
    192     DumpBuffer(f, out.Finalize());
    193     out.Reset();
    194 
    195     // Instruction address and instruction offset.
    196     out.AddFormatted("%p  %4d  ", prev_pc, prev_pc - begin);
    197 
    198     // Instruction.
    199     out.AddFormatted("%s", decode_buffer.start());
    200 
    201     // Print all the reloc info for this instruction which are not comments.
    202     for (int i = 0; i < pcs.length(); i++) {
    203       // Put together the reloc info
    204       RelocInfo relocinfo(pcs[i], rmodes[i], datas[i]);
    205 
    206       // Indent the printing of the reloc info.
    207       if (i == 0) {
    208         // The first reloc info is printed after the disassembled instruction.
    209         out.AddPadding(' ', kRelocInfoPosition - out.position());
    210       } else {
    211         // Additional reloc infos are printed on separate lines.
    212         out.AddFormatted("\n");
    213         out.AddPadding(' ', kRelocInfoPosition);
    214       }
    215 
    216       RelocInfo::Mode rmode = relocinfo.rmode();
    217       if (RelocInfo::IsPosition(rmode)) {
    218         if (RelocInfo::IsStatementPosition(rmode)) {
    219           out.AddFormatted("    ;; debug: statement %d", relocinfo.data());
    220         } else {
    221           out.AddFormatted("    ;; debug: position %d", relocinfo.data());
    222         }
    223       } else if (rmode == RelocInfo::EMBEDDED_OBJECT) {
    224         HeapStringAllocator allocator;
    225         StringStream accumulator(&allocator);
    226         relocinfo.target_object()->ShortPrint(&accumulator);
    227         SmartPointer<const char> obj_name = accumulator.ToCString();
    228         out.AddFormatted("    ;; object: %s", *obj_name);
    229       } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
    230         const char* reference_name =
    231             ref_encoder.NameOfAddress(*relocinfo.target_reference_address());
    232         out.AddFormatted("    ;; external reference (%s)", reference_name);
    233       } else if (RelocInfo::IsCodeTarget(rmode)) {
    234         out.AddFormatted("    ;; code:");
    235         if (rmode == RelocInfo::CONSTRUCT_CALL) {
    236           out.AddFormatted(" constructor,");
    237         }
    238         Code* code = Code::GetCodeFromTargetAddress(relocinfo.target_address());
    239         Code::Kind kind = code->kind();
    240         if (code->is_inline_cache_stub()) {
    241           if (rmode == RelocInfo::CODE_TARGET_CONTEXT) {
    242             out.AddFormatted(" contextual,");
    243           }
    244           InlineCacheState ic_state = code->ic_state();
    245           out.AddFormatted(" %s, %s", Code::Kind2String(kind),
    246               Code::ICState2String(ic_state));
    247           if (ic_state == MONOMORPHIC) {
    248             PropertyType type = code->type();
    249             out.AddFormatted(", %s", Code::PropertyType2String(type));
    250           }
    251           if (code->ic_in_loop() == IN_LOOP) {
    252             out.AddFormatted(", in_loop");
    253           }
    254           if (kind == Code::CALL_IC || kind == Code::KEYED_CALL_IC) {
    255             out.AddFormatted(", argc = %d", code->arguments_count());
    256           }
    257         } else if (kind == Code::STUB) {
    258           // Reverse lookup required as the minor key cannot be retrieved
    259           // from the code object.
    260           Object* obj = heap->code_stubs()->SlowReverseLookup(code);
    261           if (obj != heap->undefined_value()) {
    262             ASSERT(obj->IsSmi());
    263             // Get the STUB key and extract major and minor key.
    264             uint32_t key = Smi::cast(obj)->value();
    265             uint32_t minor_key = CodeStub::MinorKeyFromKey(key);
    266             CodeStub::Major major_key = CodeStub::GetMajorKey(code);
    267             ASSERT(major_key == CodeStub::MajorKeyFromKey(key));
    268             out.AddFormatted(" %s, %s, ",
    269                              Code::Kind2String(kind),
    270                              CodeStub::MajorName(major_key, false));
    271             switch (major_key) {
    272               case CodeStub::CallFunction: {
    273                 int argc =
    274                     CallFunctionStub::ExtractArgcFromMinorKey(minor_key);
    275                 out.AddFormatted("argc = %d", argc);
    276                 break;
    277               }
    278               default:
    279                 out.AddFormatted("minor: %d", minor_key);
    280             }
    281           }
    282         } else {
    283           out.AddFormatted(" %s", Code::Kind2String(kind));
    284         }
    285       } else if (rmode == RelocInfo::RUNTIME_ENTRY &&
    286                  Isolate::Current()->deoptimizer_data() != NULL) {
    287         // A runtime entry reloinfo might be a deoptimization bailout.
    288         Address addr = relocinfo.target_address();
    289         int id = Deoptimizer::GetDeoptimizationId(addr, Deoptimizer::EAGER);
    290         if (id == Deoptimizer::kNotDeoptimizationEntry) {
    291           out.AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
    292         } else {
    293           out.AddFormatted("    ;; deoptimization bailout %d", id);
    294         }
    295       } else {
    296         out.AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
    297       }
    298     }
    299     out.AddString("\n");
    300     DumpBuffer(f, out.Finalize());
    301     out.Reset();
    302   }
    303 
    304   delete it;
    305   return static_cast<int>(pc - begin);
    306 }
    307 
    308 
    309 int Disassembler::Decode(FILE* f, byte* begin, byte* end) {
    310   V8NameConverter defaultConverter(NULL);
    311   return DecodeIt(f, defaultConverter, begin, end);
    312 }
    313 
    314 
    315 // Called by Code::CodePrint.
    316 void Disassembler::Decode(FILE* f, Code* code) {
    317   int decode_size = (code->kind() == Code::OPTIMIZED_FUNCTION)
    318       ? static_cast<int>(code->safepoint_table_offset())
    319       : code->instruction_size();
    320   // If there might be a stack check table, stop before reaching it.
    321   if (code->kind() == Code::FUNCTION) {
    322     decode_size =
    323         Min(decode_size, static_cast<int>(code->stack_check_table_offset()));
    324   }
    325 
    326   byte* begin = code->instruction_start();
    327   byte* end = begin + decode_size;
    328   V8NameConverter v8NameConverter(code);
    329   DecodeIt(f, v8NameConverter, begin, end);
    330 }
    331 
    332 #else  // ENABLE_DISASSEMBLER
    333 
    334 void Disassembler::Dump(FILE* f, byte* begin, byte* end) {}
    335 int Disassembler::Decode(FILE* f, byte* begin, byte* end) { return 0; }
    336 void Disassembler::Decode(FILE* f, Code* code) {}
    337 
    338 #endif  // ENABLE_DISASSEMBLER
    339 
    340 } }  // namespace v8::internal
    341