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