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       PrintF(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 = code_->GetIsolate()->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, StringBuilder* out) {
    101   if (f == NULL) {
    102     PrintF("%s\n", out->Finalize());
    103   } else {
    104     PrintF(f, "%s\n", out->Finalize());
    105   }
    106   out->Reset();
    107 }
    108 
    109 
    110 static const int kOutBufferSize = 2048 + String::kMaxShortPrintLength;
    111 static const int kRelocInfoPosition = 57;
    112 
    113 static int DecodeIt(Isolate* isolate,
    114                     FILE* f,
    115                     const V8NameConverter& converter,
    116                     byte* begin,
    117                     byte* end) {
    118   SealHandleScope shs(isolate);
    119   DisallowHeapAllocation no_alloc;
    120   ExternalReferenceEncoder ref_encoder(isolate);
    121   Heap* heap = isolate->heap();
    122 
    123   v8::internal::EmbeddedVector<char, 128> decode_buffer;
    124   v8::internal::EmbeddedVector<char, kOutBufferSize> out_buffer;
    125   StringBuilder out(out_buffer.start(), out_buffer.length());
    126   byte* pc = begin;
    127   disasm::Disassembler d(converter);
    128   RelocIterator* it = NULL;
    129   if (converter.code() != NULL) {
    130     it = new RelocIterator(converter.code());
    131   } else {
    132     // No relocation information when printing code stubs.
    133   }
    134   int constants = -1;  // no constants being decoded at the start
    135 
    136   while (pc < end) {
    137     // First decode instruction so that we know its length.
    138     byte* prev_pc = pc;
    139     if (constants > 0) {
    140       OS::SNPrintF(decode_buffer,
    141                    "%08x       constant",
    142                    *reinterpret_cast<int32_t*>(pc));
    143       constants--;
    144       pc += 4;
    145     } else {
    146       int num_const = d.ConstantPoolSizeAt(pc);
    147       if (num_const >= 0) {
    148         OS::SNPrintF(decode_buffer,
    149                      "%08x       constant pool begin",
    150                      *reinterpret_cast<int32_t*>(pc));
    151         constants = num_const;
    152         pc += 4;
    153       } else if (it != NULL && !it->done() && it->rinfo()->pc() == pc &&
    154           it->rinfo()->rmode() == RelocInfo::INTERNAL_REFERENCE) {
    155         // raw pointer embedded in code stream, e.g., jump table
    156         byte* ptr = *reinterpret_cast<byte**>(pc);
    157         OS::SNPrintF(decode_buffer,
    158                      "%08" V8PRIxPTR "      jump table entry %4" V8PRIdPTR,
    159                      ptr,
    160                      ptr - begin);
    161         pc += 4;
    162       } else {
    163         decode_buffer[0] = '\0';
    164         pc += d.InstructionDecode(decode_buffer, pc);
    165       }
    166     }
    167 
    168     // Collect RelocInfo for this instruction (prev_pc .. pc-1)
    169     List<const char*> comments(4);
    170     List<byte*> pcs(1);
    171     List<RelocInfo::Mode> rmodes(1);
    172     List<intptr_t> datas(1);
    173     if (it != NULL) {
    174       while (!it->done() && it->rinfo()->pc() < pc) {
    175         if (RelocInfo::IsComment(it->rinfo()->rmode())) {
    176           // For comments just collect the text.
    177           comments.Add(reinterpret_cast<const char*>(it->rinfo()->data()));
    178         } else {
    179           // For other reloc info collect all data.
    180           pcs.Add(it->rinfo()->pc());
    181           rmodes.Add(it->rinfo()->rmode());
    182           datas.Add(it->rinfo()->data());
    183         }
    184         it->next();
    185       }
    186     }
    187 
    188     // Comments.
    189     for (int i = 0; i < comments.length(); i++) {
    190       out.AddFormatted("                  %s", comments[i]);
    191       DumpBuffer(f, &out);
    192     }
    193 
    194     // Instruction address and instruction offset.
    195     out.AddFormatted("%p  %4d  ", prev_pc, prev_pc - begin);
    196 
    197     // Instruction.
    198     out.AddFormatted("%s", decode_buffer.start());
    199 
    200     // Print all the reloc info for this instruction which are not comments.
    201     for (int i = 0; i < pcs.length(); i++) {
    202       // Put together the reloc info
    203       RelocInfo relocinfo(pcs[i], rmodes[i], datas[i], NULL);
    204 
    205       // Indent the printing of the reloc info.
    206       if (i == 0) {
    207         // The first reloc info is printed after the disassembled instruction.
    208         out.AddPadding(' ', kRelocInfoPosition - out.position());
    209       } else {
    210         // Additional reloc infos are printed on separate lines.
    211         DumpBuffer(f, &out);
    212         out.AddPadding(' ', kRelocInfoPosition);
    213       }
    214 
    215       RelocInfo::Mode rmode = relocinfo.rmode();
    216       if (RelocInfo::IsPosition(rmode)) {
    217         if (RelocInfo::IsStatementPosition(rmode)) {
    218           out.AddFormatted("    ;; debug: statement %d", relocinfo.data());
    219         } else {
    220           out.AddFormatted("    ;; debug: position %d", relocinfo.data());
    221         }
    222       } else if (rmode == RelocInfo::EMBEDDED_OBJECT) {
    223         HeapStringAllocator allocator;
    224         StringStream accumulator(&allocator);
    225         relocinfo.target_object()->ShortPrint(&accumulator);
    226         SmartArrayPointer<const char> obj_name = accumulator.ToCString();
    227         out.AddFormatted("    ;; object: %s", *obj_name);
    228       } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
    229         const char* reference_name =
    230             ref_encoder.NameOfAddress(relocinfo.target_reference());
    231         out.AddFormatted("    ;; external reference (%s)", reference_name);
    232       } else if (RelocInfo::IsCodeTarget(rmode)) {
    233         out.AddFormatted("    ;; code:");
    234         if (rmode == RelocInfo::CONSTRUCT_CALL) {
    235           out.AddFormatted(" constructor,");
    236         }
    237         Code* code = Code::GetCodeFromTargetAddress(relocinfo.target_address());
    238         Code::Kind kind = code->kind();
    239         if (code->is_inline_cache_stub()) {
    240           if (rmode == RelocInfo::CODE_TARGET_CONTEXT) {
    241             out.AddFormatted(" contextual,");
    242           }
    243           InlineCacheState ic_state = code->ic_state();
    244           out.AddFormatted(" %s, %s", Code::Kind2String(kind),
    245               Code::ICState2String(ic_state));
    246           if (ic_state == MONOMORPHIC) {
    247             Code::StubType type = code->type();
    248             out.AddFormatted(", %s", Code::StubType2String(type));
    249           }
    250           if (kind == Code::CALL_IC || kind == Code::KEYED_CALL_IC) {
    251             out.AddFormatted(", argc = %d", code->arguments_count());
    252           }
    253         } else if (kind == Code::STUB || kind == Code::HANDLER) {
    254           // Reverse lookup required as the minor key cannot be retrieved
    255           // from the code object.
    256           Object* obj = heap->code_stubs()->SlowReverseLookup(code);
    257           if (obj != heap->undefined_value()) {
    258             ASSERT(obj->IsSmi());
    259             // Get the STUB key and extract major and minor key.
    260             uint32_t key = Smi::cast(obj)->value();
    261             uint32_t minor_key = CodeStub::MinorKeyFromKey(key);
    262             CodeStub::Major major_key = CodeStub::GetMajorKey(code);
    263             ASSERT(major_key == CodeStub::MajorKeyFromKey(key));
    264             out.AddFormatted(" %s, %s, ",
    265                              Code::Kind2String(kind),
    266                              CodeStub::MajorName(major_key, false));
    267             switch (major_key) {
    268               case CodeStub::CallFunction: {
    269                 int argc =
    270                     CallFunctionStub::ExtractArgcFromMinorKey(minor_key);
    271                 out.AddFormatted("argc = %d", argc);
    272                 break;
    273               }
    274               default:
    275                 out.AddFormatted("minor: %d", minor_key);
    276             }
    277           }
    278         } else {
    279           out.AddFormatted(" %s", Code::Kind2String(kind));
    280         }
    281         if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
    282           out.AddFormatted(" (id = %d)", static_cast<int>(relocinfo.data()));
    283         }
    284       } else if (RelocInfo::IsRuntimeEntry(rmode) &&
    285                  isolate->deoptimizer_data() != NULL) {
    286         // A runtime entry reloinfo might be a deoptimization bailout.
    287         Address addr = relocinfo.target_address();
    288         int id = Deoptimizer::GetDeoptimizationId(isolate,
    289                                                   addr,
    290                                                   Deoptimizer::EAGER);
    291         if (id == Deoptimizer::kNotDeoptimizationEntry) {
    292           id = Deoptimizer::GetDeoptimizationId(isolate,
    293                                                 addr,
    294                                                 Deoptimizer::LAZY);
    295           if (id == Deoptimizer::kNotDeoptimizationEntry) {
    296             id = Deoptimizer::GetDeoptimizationId(isolate,
    297                                                   addr,
    298                                                   Deoptimizer::SOFT);
    299             if (id == Deoptimizer::kNotDeoptimizationEntry) {
    300               out.AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
    301             } else {
    302               out.AddFormatted("    ;; soft deoptimization bailout %d", id);
    303             }
    304           } else {
    305             out.AddFormatted("    ;; lazy deoptimization bailout %d", id);
    306           }
    307         } else {
    308           out.AddFormatted("    ;; deoptimization bailout %d", id);
    309         }
    310       } else {
    311         out.AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
    312       }
    313     }
    314     DumpBuffer(f, &out);
    315   }
    316 
    317   // Emit comments following the last instruction (if any).
    318   if (it != NULL) {
    319     for ( ; !it->done(); it->next()) {
    320       if (RelocInfo::IsComment(it->rinfo()->rmode())) {
    321         out.AddFormatted("                  %s",
    322                          reinterpret_cast<const char*>(it->rinfo()->data()));
    323         DumpBuffer(f, &out);
    324       }
    325     }
    326   }
    327 
    328   delete it;
    329   return static_cast<int>(pc - begin);
    330 }
    331 
    332 
    333 int Disassembler::Decode(Isolate* isolate, FILE* f, byte* begin, byte* end) {
    334   V8NameConverter defaultConverter(NULL);
    335   return DecodeIt(isolate, f, defaultConverter, begin, end);
    336 }
    337 
    338 
    339 // Called by Code::CodePrint.
    340 void Disassembler::Decode(FILE* f, Code* code) {
    341   Isolate* isolate = code->GetIsolate();
    342   int decode_size = code->is_crankshafted()
    343       ? static_cast<int>(code->safepoint_table_offset())
    344       : code->instruction_size();
    345   // If there might be a back edge table, stop before reaching it.
    346   if (code->kind() == Code::FUNCTION) {
    347     decode_size =
    348         Min(decode_size, static_cast<int>(code->back_edge_table_offset()));
    349   }
    350 
    351   byte* begin = code->instruction_start();
    352   byte* end = begin + decode_size;
    353   V8NameConverter v8NameConverter(code);
    354   DecodeIt(isolate, f, v8NameConverter, begin, end);
    355 }
    356 
    357 #else  // ENABLE_DISASSEMBLER
    358 
    359 void Disassembler::Dump(FILE* f, byte* begin, byte* end) {}
    360 int Disassembler::Decode(Isolate* isolate, FILE* f, byte* begin, byte* end) {
    361   return 0;
    362 }
    363 
    364 
    365 void Disassembler::Decode(FILE* f, Code* code) {}
    366 
    367 #endif  // ENABLE_DISASSEMBLER
    368 
    369 } }  // namespace v8::internal
    370