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