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