// Copyright 2006-2008 the V8 project authors. All rights reserved. // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // * Neither the name of Google Inc. nor the names of its // contributors may be used to endorse or promote products derived // from this software without specific prior written permission. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "v8.h" #include "code-stubs.h" #include "codegen-inl.h" #include "debug.h" #include "disasm.h" #include "disassembler.h" #include "macro-assembler.h" #include "serialize.h" #include "string-stream.h" namespace v8 { namespace internal { #ifdef ENABLE_DISASSEMBLER void Disassembler::Dump(FILE* f, byte* begin, byte* end) { for (byte* pc = begin; pc < end; pc++) { if (f == NULL) { PrintF("%" V8PRIxPTR " %4" V8PRIdPTR " %02x\n", pc, pc - begin, *pc); } else { fprintf(f, "%" V8PRIxPTR " %4" V8PRIdPTR " %02x\n", reinterpret_cast(pc), pc - begin, *pc); } } } class V8NameConverter: public disasm::NameConverter { public: explicit V8NameConverter(Code* code) : code_(code) {} virtual const char* NameOfAddress(byte* pc) const; virtual const char* NameInCode(byte* addr) const; Code* code() const { return code_; } private: Code* code_; }; const char* V8NameConverter::NameOfAddress(byte* pc) const { static v8::internal::EmbeddedVector buffer; const char* name = Builtins::Lookup(pc); if (name != NULL) { OS::SNPrintF(buffer, "%s (%p)", name, pc); return buffer.start(); } if (code_ != NULL) { int offs = pc - code_->instruction_start(); // print as code offset, if it seems reasonable if (0 <= offs && offs < code_->instruction_size()) { OS::SNPrintF(buffer, "%d (%p)", offs, pc); return buffer.start(); } } return disasm::NameConverter::NameOfAddress(pc); } const char* V8NameConverter::NameInCode(byte* addr) const { // The V8NameConverter is used for well known code, so we can "safely" // dereference pointers in generated code. return (code_ != NULL) ? reinterpret_cast(addr) : ""; } static void DumpBuffer(FILE* f, char* buff) { if (f == NULL) { PrintF("%s", buff); } else { fprintf(f, "%s", buff); } } static const int kOutBufferSize = 2048 + String::kMaxShortPrintLength; static const int kRelocInfoPosition = 57; static int DecodeIt(FILE* f, const V8NameConverter& converter, byte* begin, byte* end) { NoHandleAllocation ha; AssertNoAllocation no_alloc; ExternalReferenceEncoder ref_encoder; v8::internal::EmbeddedVector decode_buffer; v8::internal::EmbeddedVector out_buffer; byte* pc = begin; disasm::Disassembler d(converter); RelocIterator* it = NULL; if (converter.code() != NULL) { it = new RelocIterator(converter.code()); } else { // No relocation information when printing code stubs. } int constants = -1; // no constants being decoded at the start while (pc < end) { // First decode instruction so that we know its length. byte* prev_pc = pc; if (constants > 0) { OS::SNPrintF(decode_buffer, "%08x constant", *reinterpret_cast(pc)); constants--; pc += 4; } else { int num_const = d.ConstantPoolSizeAt(pc); if (num_const >= 0) { OS::SNPrintF(decode_buffer, "%08x constant pool begin", *reinterpret_cast(pc)); constants = num_const; pc += 4; } else if (it != NULL && !it->done() && it->rinfo()->pc() == pc && it->rinfo()->rmode() == RelocInfo::INTERNAL_REFERENCE) { // raw pointer embedded in code stream, e.g., jump table byte* ptr = *reinterpret_cast(pc); OS::SNPrintF(decode_buffer, "%08" V8PRIxPTR " jump table entry %4" V8PRIdPTR, ptr, ptr - begin); pc += 4; } else { decode_buffer[0] = '\0'; pc += d.InstructionDecode(decode_buffer, pc); } } // Collect RelocInfo for this instruction (prev_pc .. pc-1) List comments(4); List pcs(1); List rmodes(1); List datas(1); if (it != NULL) { while (!it->done() && it->rinfo()->pc() < pc) { if (RelocInfo::IsComment(it->rinfo()->rmode())) { // For comments just collect the text. comments.Add(reinterpret_cast(it->rinfo()->data())); } else { // For other reloc info collect all data. pcs.Add(it->rinfo()->pc()); rmodes.Add(it->rinfo()->rmode()); datas.Add(it->rinfo()->data()); } it->next(); } } StringBuilder out(out_buffer.start(), out_buffer.length()); // Comments. for (int i = 0; i < comments.length(); i++) { out.AddFormatted(" %s\n", comments[i]); } // Write out comments, resets outp so that we can format the next line. DumpBuffer(f, out.Finalize()); out.Reset(); // Instruction address and instruction offset. out.AddFormatted("%p %4d ", prev_pc, prev_pc - begin); // Instruction. out.AddFormatted("%s", decode_buffer.start()); // Print all the reloc info for this instruction which are not comments. for (int i = 0; i < pcs.length(); i++) { // Put together the reloc info RelocInfo relocinfo(pcs[i], rmodes[i], datas[i]); // Indent the printing of the reloc info. if (i == 0) { // The first reloc info is printed after the disassembled instruction. out.AddPadding(' ', kRelocInfoPosition - out.position()); } else { // Additional reloc infos are printed on separate lines. out.AddFormatted("\n"); out.AddPadding(' ', kRelocInfoPosition); } RelocInfo::Mode rmode = relocinfo.rmode(); if (RelocInfo::IsPosition(rmode)) { if (RelocInfo::IsStatementPosition(rmode)) { out.AddFormatted(" ;; debug: statement %d", relocinfo.data()); } else { out.AddFormatted(" ;; debug: position %d", relocinfo.data()); } } else if (rmode == RelocInfo::EMBEDDED_OBJECT) { HeapStringAllocator allocator; StringStream accumulator(&allocator); relocinfo.target_object()->ShortPrint(&accumulator); SmartPointer obj_name = accumulator.ToCString(); out.AddFormatted(" ;; object: %s", *obj_name); } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) { const char* reference_name = ref_encoder.NameOfAddress(*relocinfo.target_reference_address()); out.AddFormatted(" ;; external reference (%s)", reference_name); } else if (RelocInfo::IsCodeTarget(rmode)) { out.AddFormatted(" ;; code:"); if (rmode == RelocInfo::CONSTRUCT_CALL) { out.AddFormatted(" constructor,"); } Code* code = Code::GetCodeFromTargetAddress(relocinfo.target_address()); Code::Kind kind = code->kind(); if (code->is_inline_cache_stub()) { if (rmode == RelocInfo::CODE_TARGET_CONTEXT) { out.AddFormatted(" contextual,"); } InlineCacheState ic_state = code->ic_state(); out.AddFormatted(" %s, %s", Code::Kind2String(kind), Code::ICState2String(ic_state)); if (ic_state == MONOMORPHIC) { PropertyType type = code->type(); out.AddFormatted(", %s", Code::PropertyType2String(type)); } if (code->ic_in_loop() == IN_LOOP) { out.AddFormatted(", in_loop"); } if (kind == Code::CALL_IC) { out.AddFormatted(", argc = %d", code->arguments_count()); } } else if (kind == Code::STUB) { // Reverse lookup required as the minor key cannot be retrieved // from the code object. Object* obj = Heap::code_stubs()->SlowReverseLookup(code); if (obj != Heap::undefined_value()) { ASSERT(obj->IsSmi()); // Get the STUB key and extract major and minor key. uint32_t key = Smi::cast(obj)->value(); uint32_t minor_key = CodeStub::MinorKeyFromKey(key); ASSERT(code->major_key() == CodeStub::MajorKeyFromKey(key)); out.AddFormatted(" %s, %s, ", Code::Kind2String(kind), CodeStub::MajorName(code->major_key())); switch (code->major_key()) { case CodeStub::CallFunction: out.AddFormatted("argc = %d", minor_key); break; case CodeStub::Runtime: { const char* name = RuntimeStub::GetNameFromMinorKey(minor_key); out.AddFormatted("%s", name); break; } default: out.AddFormatted("minor: %d", minor_key); } } } else { out.AddFormatted(" %s", Code::Kind2String(kind)); } } else { out.AddFormatted(" ;; %s", RelocInfo::RelocModeName(rmode)); } } out.AddString("\n"); DumpBuffer(f, out.Finalize()); out.Reset(); } delete it; return pc - begin; } int Disassembler::Decode(FILE* f, byte* begin, byte* end) { V8NameConverter defaultConverter(NULL); return DecodeIt(f, defaultConverter, begin, end); } // Called by Code::CodePrint. void Disassembler::Decode(FILE* f, Code* code) { byte* begin = Code::cast(code)->instruction_start(); byte* end = begin + Code::cast(code)->instruction_size(); V8NameConverter v8NameConverter(code); DecodeIt(f, v8NameConverter, begin, end); } #else // ENABLE_DISASSEMBLER void Disassembler::Dump(FILE* f, byte* begin, byte* end) {} int Disassembler::Decode(FILE* f, byte* begin, byte* end) { return 0; } void Disassembler::Decode(FILE* f, Code* code) {} #endif // ENABLE_DISASSEMBLER } } // namespace v8::internal