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