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