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 40namespace v8 { 41namespace internal { 42 43#ifdef ENABLE_DISASSEMBLER 44 45void 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 fprintf(f, "%" V8PRIxPTR " %4" V8PRIdPTR " %02x\n", 54 reinterpret_cast<uintptr_t>(pc), pc - begin, *pc); 55 } 56 } 57} 58 59 60class 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 73const char* V8NameConverter::NameOfAddress(byte* pc) const { 74 const char* name = Isolate::Current()->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 93const 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 100static void DumpBuffer(FILE* f, StringBuilder* out) { 101 if (f == NULL) { 102 PrintF("%s\n", out->Finalize()); 103 } else { 104 fprintf(f, "%s\n", out->Finalize()); 105 } 106 out->Reset(); 107} 108 109 110 111static const int kOutBufferSize = 2048 + String::kMaxShortPrintLength; 112static const int kRelocInfoPosition = 57; 113 114static int DecodeIt(FILE* f, 115 const V8NameConverter& converter, 116 byte* begin, 117 byte* end) { 118 NoHandleAllocation ha; 119 AssertNoAllocation no_alloc; 120 ExternalReferenceEncoder ref_encoder; 121 Heap* heap = 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_address()); 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 PropertyType type = code->type(); 248 out.AddFormatted(", %s", Code::PropertyType2String(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) { 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 (rmode == RelocInfo::RUNTIME_ENTRY && 285 Isolate::Current()->deoptimizer_data() != NULL) { 286 // A runtime entry reloinfo might be a deoptimization bailout. 287 Address addr = relocinfo.target_address(); 288 int id = Deoptimizer::GetDeoptimizationId(addr, Deoptimizer::EAGER); 289 if (id == Deoptimizer::kNotDeoptimizationEntry) { 290 out.AddFormatted(" ;; %s", RelocInfo::RelocModeName(rmode)); 291 } else { 292 out.AddFormatted(" ;; deoptimization bailout %d", id); 293 } 294 } else { 295 out.AddFormatted(" ;; %s", RelocInfo::RelocModeName(rmode)); 296 } 297 } 298 DumpBuffer(f, &out); 299 } 300 301 // Emit comments following the last instruction (if any). 302 if (it != NULL) { 303 for ( ; !it->done(); it->next()) { 304 if (RelocInfo::IsComment(it->rinfo()->rmode())) { 305 out.AddFormatted(" %s", 306 reinterpret_cast<const char*>(it->rinfo()->data())); 307 DumpBuffer(f, &out); 308 } 309 } 310 } 311 312 delete it; 313 return static_cast<int>(pc - begin); 314} 315 316 317int Disassembler::Decode(FILE* f, byte* begin, byte* end) { 318 V8NameConverter defaultConverter(NULL); 319 return DecodeIt(f, defaultConverter, begin, end); 320} 321 322 323// Called by Code::CodePrint. 324void Disassembler::Decode(FILE* f, Code* code) { 325 int decode_size = (code->kind() == Code::OPTIMIZED_FUNCTION) 326 ? static_cast<int>(code->safepoint_table_offset()) 327 : code->instruction_size(); 328 // If there might be a stack check table, stop before reaching it. 329 if (code->kind() == Code::FUNCTION) { 330 decode_size = 331 Min(decode_size, static_cast<int>(code->stack_check_table_offset())); 332 } 333 334 byte* begin = code->instruction_start(); 335 byte* end = begin + decode_size; 336 V8NameConverter v8NameConverter(code); 337 DecodeIt(f, v8NameConverter, begin, end); 338} 339 340#else // ENABLE_DISASSEMBLER 341 342void Disassembler::Dump(FILE* f, byte* begin, byte* end) {} 343int Disassembler::Decode(FILE* f, byte* begin, byte* end) { return 0; } 344void Disassembler::Decode(FILE* f, Code* code) {} 345 346#endif // ENABLE_DISASSEMBLER 347 348} } // namespace v8::internal 349