Home | History | Annotate | Download | only in src
      1 // Copyright 2011 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/disassembler.h"
      6 
      7 #include <memory>
      8 
      9 #include "src/assembler-inl.h"
     10 #include "src/code-stubs.h"
     11 #include "src/codegen.h"
     12 #include "src/debug/debug.h"
     13 #include "src/deoptimizer.h"
     14 #include "src/disasm.h"
     15 #include "src/ic/ic.h"
     16 #include "src/macro-assembler.h"
     17 #include "src/objects-inl.h"
     18 #include "src/snapshot/serializer-common.h"
     19 #include "src/string-stream.h"
     20 
     21 namespace v8 {
     22 namespace internal {
     23 
     24 #ifdef ENABLE_DISASSEMBLER
     25 
     26 class V8NameConverter: public disasm::NameConverter {
     27  public:
     28   explicit V8NameConverter(Code* code) : code_(code) {}
     29   virtual const char* NameOfAddress(byte* pc) const;
     30   virtual const char* NameInCode(byte* addr) const;
     31   Code* code() const { return code_; }
     32  private:
     33   Code* code_;
     34 
     35   EmbeddedVector<char, 128> v8_buffer_;
     36 };
     37 
     38 
     39 const char* V8NameConverter::NameOfAddress(byte* pc) const {
     40   const char* name =
     41       code_ == NULL ? NULL : code_->GetIsolate()->builtins()->Lookup(pc);
     42 
     43   if (name != NULL) {
     44     SNPrintF(v8_buffer_, "%s  (%p)", name, static_cast<void*>(pc));
     45     return v8_buffer_.start();
     46   }
     47 
     48   if (code_ != NULL) {
     49     int offs = static_cast<int>(pc - code_->instruction_start());
     50     // print as code offset, if it seems reasonable
     51     if (0 <= offs && offs < code_->instruction_size()) {
     52       SNPrintF(v8_buffer_, "%d  (%p)", offs, static_cast<void*>(pc));
     53       return v8_buffer_.start();
     54     }
     55   }
     56 
     57   return disasm::NameConverter::NameOfAddress(pc);
     58 }
     59 
     60 
     61 const char* V8NameConverter::NameInCode(byte* addr) const {
     62   // The V8NameConverter is used for well known code, so we can "safely"
     63   // dereference pointers in generated code.
     64   return (code_ != NULL) ? reinterpret_cast<const char*>(addr) : "";
     65 }
     66 
     67 
     68 static void DumpBuffer(std::ostream* os, StringBuilder* out) {
     69   (*os) << out->Finalize() << std::endl;
     70   out->Reset();
     71 }
     72 
     73 
     74 static const int kOutBufferSize = 2048 + String::kMaxShortPrintLength;
     75 static const int kRelocInfoPosition = 57;
     76 
     77 static int DecodeIt(Isolate* isolate, std::ostream* os,
     78                     const V8NameConverter& converter, byte* begin, byte* end) {
     79   SealHandleScope shs(isolate);
     80   DisallowHeapAllocation no_alloc;
     81   ExternalReferenceEncoder ref_encoder(isolate);
     82 
     83   v8::internal::EmbeddedVector<char, 128> decode_buffer;
     84   v8::internal::EmbeddedVector<char, kOutBufferSize> out_buffer;
     85   StringBuilder out(out_buffer.start(), out_buffer.length());
     86   byte* pc = begin;
     87   disasm::Disassembler d(converter);
     88   RelocIterator* it = NULL;
     89   if (converter.code() != NULL) {
     90     it = new RelocIterator(converter.code());
     91   } else {
     92     // No relocation information when printing code stubs.
     93   }
     94   int constants = -1;  // no constants being decoded at the start
     95 
     96   while (pc < end) {
     97     // First decode instruction so that we know its length.
     98     byte* prev_pc = pc;
     99     if (constants > 0) {
    100       SNPrintF(decode_buffer,
    101                "%08x       constant",
    102                *reinterpret_cast<int32_t*>(pc));
    103       constants--;
    104       pc += 4;
    105     } else {
    106       int num_const = d.ConstantPoolSizeAt(pc);
    107       if (num_const >= 0) {
    108         SNPrintF(decode_buffer,
    109                  "%08x       constant pool begin (num_const = %d)",
    110                  *reinterpret_cast<int32_t*>(pc), num_const);
    111         constants = num_const;
    112         pc += 4;
    113       } else if (it != NULL && !it->done() && it->rinfo()->pc() == pc &&
    114           it->rinfo()->rmode() == RelocInfo::INTERNAL_REFERENCE) {
    115         // raw pointer embedded in code stream, e.g., jump table
    116         byte* ptr = *reinterpret_cast<byte**>(pc);
    117         SNPrintF(
    118             decode_buffer, "%08" V8PRIxPTR "      jump table entry %4" PRIuS,
    119             reinterpret_cast<intptr_t>(ptr), static_cast<size_t>(ptr - begin));
    120         pc += sizeof(ptr);
    121       } else {
    122         decode_buffer[0] = '\0';
    123         pc += d.InstructionDecode(decode_buffer, pc);
    124       }
    125     }
    126 
    127     // Collect RelocInfo for this instruction (prev_pc .. pc-1)
    128     List<const char*> comments(4);
    129     List<byte*> pcs(1);
    130     List<RelocInfo::Mode> rmodes(1);
    131     List<intptr_t> datas(1);
    132     if (it != NULL) {
    133       while (!it->done() && it->rinfo()->pc() < pc) {
    134         if (RelocInfo::IsComment(it->rinfo()->rmode())) {
    135           // For comments just collect the text.
    136           comments.Add(reinterpret_cast<const char*>(it->rinfo()->data()));
    137         } else {
    138           // For other reloc info collect all data.
    139           pcs.Add(it->rinfo()->pc());
    140           rmodes.Add(it->rinfo()->rmode());
    141           datas.Add(it->rinfo()->data());
    142         }
    143         it->next();
    144       }
    145     }
    146 
    147     // Comments.
    148     for (int i = 0; i < comments.length(); i++) {
    149       out.AddFormatted("                  %s", comments[i]);
    150       DumpBuffer(os, &out);
    151     }
    152 
    153     // Instruction address and instruction offset.
    154     out.AddFormatted("%p  %4" V8PRIdPTRDIFF "  ", static_cast<void*>(prev_pc),
    155                      prev_pc - begin);
    156 
    157     // Instruction.
    158     out.AddFormatted("%s", decode_buffer.start());
    159 
    160     // Print all the reloc info for this instruction which are not comments.
    161     for (int i = 0; i < pcs.length(); i++) {
    162       // Put together the reloc info
    163       RelocInfo relocinfo(isolate, pcs[i], rmodes[i], datas[i],
    164                           converter.code());
    165 
    166       // Indent the printing of the reloc info.
    167       if (i == 0) {
    168         // The first reloc info is printed after the disassembled instruction.
    169         out.AddPadding(' ', kRelocInfoPosition - out.position());
    170       } else {
    171         // Additional reloc infos are printed on separate lines.
    172         DumpBuffer(os, &out);
    173         out.AddPadding(' ', kRelocInfoPosition);
    174       }
    175 
    176       RelocInfo::Mode rmode = relocinfo.rmode();
    177       if (rmode == RelocInfo::DEOPT_SCRIPT_OFFSET) {
    178         out.AddFormatted("    ;; debug: deopt position, script offset '%d'",
    179                          static_cast<int>(relocinfo.data()));
    180       } else if (rmode == RelocInfo::DEOPT_INLINING_ID) {
    181         out.AddFormatted("    ;; debug: deopt position, inlining id '%d'",
    182                          static_cast<int>(relocinfo.data()));
    183       } else if (rmode == RelocInfo::DEOPT_REASON) {
    184         DeoptimizeReason reason =
    185             static_cast<DeoptimizeReason>(relocinfo.data());
    186         out.AddFormatted("    ;; debug: deopt reason '%s'",
    187                          DeoptimizeReasonToString(reason));
    188       } else if (rmode == RelocInfo::DEOPT_ID) {
    189         out.AddFormatted("    ;; debug: deopt index %d",
    190                          static_cast<int>(relocinfo.data()));
    191       } else if (rmode == RelocInfo::EMBEDDED_OBJECT) {
    192         HeapStringAllocator allocator;
    193         StringStream accumulator(&allocator);
    194         relocinfo.target_object()->ShortPrint(&accumulator);
    195         std::unique_ptr<char[]> obj_name = accumulator.ToCString();
    196         out.AddFormatted("    ;; object: %s", obj_name.get());
    197       } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
    198         const char* reference_name = ref_encoder.NameOfAddress(
    199             isolate, relocinfo.target_external_reference());
    200         out.AddFormatted("    ;; external reference (%s)", reference_name);
    201       } else if (RelocInfo::IsCodeTarget(rmode)) {
    202         out.AddFormatted("    ;; code:");
    203         Code* code = Code::GetCodeFromTargetAddress(relocinfo.target_address());
    204         Code::Kind kind = code->kind();
    205         if (code->is_inline_cache_stub()) {
    206           out.AddFormatted(" %s", Code::Kind2String(kind));
    207           if (!IC::ICUseVector(kind)) {
    208             InlineCacheState ic_state = IC::StateFromCode(code);
    209             out.AddFormatted(" %s", Code::ICState2String(ic_state));
    210           }
    211         } else if (kind == Code::STUB || kind == Code::HANDLER) {
    212           // Get the STUB key and extract major and minor key.
    213           uint32_t key = code->stub_key();
    214           uint32_t minor_key = CodeStub::MinorKeyFromKey(key);
    215           CodeStub::Major major_key = CodeStub::GetMajorKey(code);
    216           DCHECK(major_key == CodeStub::MajorKeyFromKey(key));
    217           out.AddFormatted(" %s, %s, ", Code::Kind2String(kind),
    218                            CodeStub::MajorName(major_key));
    219           out.AddFormatted("minor: %d", minor_key);
    220         } else {
    221           out.AddFormatted(" %s", Code::Kind2String(kind));
    222         }
    223         if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
    224           out.AddFormatted(" (id = %d)", static_cast<int>(relocinfo.data()));
    225         }
    226       } else if (RelocInfo::IsRuntimeEntry(rmode) &&
    227                  isolate->deoptimizer_data() != NULL) {
    228         // A runtime entry reloinfo might be a deoptimization bailout.
    229         Address addr = relocinfo.target_address();
    230         int id = Deoptimizer::GetDeoptimizationId(isolate,
    231                                                   addr,
    232                                                   Deoptimizer::EAGER);
    233         if (id == Deoptimizer::kNotDeoptimizationEntry) {
    234           id = Deoptimizer::GetDeoptimizationId(isolate,
    235                                                 addr,
    236                                                 Deoptimizer::LAZY);
    237           if (id == Deoptimizer::kNotDeoptimizationEntry) {
    238             id = Deoptimizer::GetDeoptimizationId(isolate,
    239                                                   addr,
    240                                                   Deoptimizer::SOFT);
    241             if (id == Deoptimizer::kNotDeoptimizationEntry) {
    242               out.AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
    243             } else {
    244               out.AddFormatted("    ;; soft deoptimization bailout %d", id);
    245             }
    246           } else {
    247             out.AddFormatted("    ;; lazy deoptimization bailout %d", id);
    248           }
    249         } else {
    250           out.AddFormatted("    ;; deoptimization bailout %d", id);
    251         }
    252       } else {
    253         out.AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
    254       }
    255     }
    256     DumpBuffer(os, &out);
    257   }
    258 
    259   // Emit comments following the last instruction (if any).
    260   if (it != NULL) {
    261     for ( ; !it->done(); it->next()) {
    262       if (RelocInfo::IsComment(it->rinfo()->rmode())) {
    263         out.AddFormatted("                  %s",
    264                          reinterpret_cast<const char*>(it->rinfo()->data()));
    265         DumpBuffer(os, &out);
    266       }
    267     }
    268   }
    269 
    270   delete it;
    271   return static_cast<int>(pc - begin);
    272 }
    273 
    274 
    275 int Disassembler::Decode(Isolate* isolate, std::ostream* os, byte* begin,
    276                          byte* end, Code* code) {
    277   V8NameConverter v8NameConverter(code);
    278   return DecodeIt(isolate, os, v8NameConverter, begin, end);
    279 }
    280 
    281 #else  // ENABLE_DISASSEMBLER
    282 
    283 int Disassembler::Decode(Isolate* isolate, std::ostream* os, byte* begin,
    284                          byte* end, Code* code) {
    285   return 0;
    286 }
    287 
    288 #endif  // ENABLE_DISASSEMBLER
    289 
    290 }  // namespace internal
    291 }  // namespace v8
    292