• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
Dump(FILE * f,byte * begin,byte * end)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       fprintf(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:
V8NameConverter(Code * code)62   explicit V8NameConverter(Code* code) : code_(code) {}
63   virtual const char* NameOfAddress(byte* pc) const;
64   virtual const char* NameInCode(byte* addr) const;
code() const65   Code* code() const { return code_; }
66  private:
67   Code* code_;
68 
69   EmbeddedVector<char, 128> v8_buffer_;
70 };
71 
72 
NameOfAddress(byte * pc) const73 const 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 
NameInCode(byte * addr) const93 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 
DumpBuffer(FILE * f,char * buff)100 static void DumpBuffer(FILE* f, char* buff) {
101   if (f == NULL) {
102     PrintF("%s", buff);
103   } else {
104     fprintf(f, "%s", buff);
105   }
106 }
107 
108 static const int kOutBufferSize = 2048 + String::kMaxShortPrintLength;
109 static const int kRelocInfoPosition = 57;
110 
DecodeIt(FILE * f,const V8NameConverter & converter,byte * begin,byte * end)111 static int DecodeIt(FILE* f,
112                     const V8NameConverter& converter,
113                     byte* begin,
114                     byte* end) {
115   NoHandleAllocation ha;
116   AssertNoAllocation no_alloc;
117   ExternalReferenceEncoder ref_encoder;
118   Heap* heap = HEAP;
119 
120   v8::internal::EmbeddedVector<char, 128> decode_buffer;
121   v8::internal::EmbeddedVector<char, kOutBufferSize> out_buffer;
122   byte* pc = begin;
123   disasm::Disassembler d(converter);
124   RelocIterator* it = NULL;
125   if (converter.code() != NULL) {
126     it = new RelocIterator(converter.code());
127   } else {
128     // No relocation information when printing code stubs.
129   }
130   int constants = -1;  // no constants being decoded at the start
131 
132   while (pc < end) {
133     // First decode instruction so that we know its length.
134     byte* prev_pc = pc;
135     if (constants > 0) {
136       OS::SNPrintF(decode_buffer,
137                    "%08x       constant",
138                    *reinterpret_cast<int32_t*>(pc));
139       constants--;
140       pc += 4;
141     } else {
142       int num_const = d.ConstantPoolSizeAt(pc);
143       if (num_const >= 0) {
144         OS::SNPrintF(decode_buffer,
145                      "%08x       constant pool begin",
146                      *reinterpret_cast<int32_t*>(pc));
147         constants = num_const;
148         pc += 4;
149       } else if (it != NULL && !it->done() && it->rinfo()->pc() == pc &&
150           it->rinfo()->rmode() == RelocInfo::INTERNAL_REFERENCE) {
151         // raw pointer embedded in code stream, e.g., jump table
152         byte* ptr = *reinterpret_cast<byte**>(pc);
153         OS::SNPrintF(decode_buffer,
154                      "%08" V8PRIxPTR "      jump table entry %4" V8PRIdPTR,
155                      ptr,
156                      ptr - begin);
157         pc += 4;
158       } else {
159         decode_buffer[0] = '\0';
160         pc += d.InstructionDecode(decode_buffer, pc);
161       }
162     }
163 
164     // Collect RelocInfo for this instruction (prev_pc .. pc-1)
165     List<const char*> comments(4);
166     List<byte*> pcs(1);
167     List<RelocInfo::Mode> rmodes(1);
168     List<intptr_t> datas(1);
169     if (it != NULL) {
170       while (!it->done() && it->rinfo()->pc() < pc) {
171         if (RelocInfo::IsComment(it->rinfo()->rmode())) {
172           // For comments just collect the text.
173           comments.Add(reinterpret_cast<const char*>(it->rinfo()->data()));
174         } else {
175           // For other reloc info collect all data.
176           pcs.Add(it->rinfo()->pc());
177           rmodes.Add(it->rinfo()->rmode());
178           datas.Add(it->rinfo()->data());
179         }
180         it->next();
181       }
182     }
183 
184     StringBuilder out(out_buffer.start(), out_buffer.length());
185 
186     // Comments.
187     for (int i = 0; i < comments.length(); i++) {
188       out.AddFormatted("                  %s\n", comments[i]);
189     }
190 
191     // Write out comments, resets outp so that we can format the next line.
192     DumpBuffer(f, out.Finalize());
193     out.Reset();
194 
195     // Instruction address and instruction offset.
196     out.AddFormatted("%p  %4d  ", prev_pc, prev_pc - begin);
197 
198     // Instruction.
199     out.AddFormatted("%s", decode_buffer.start());
200 
201     // Print all the reloc info for this instruction which are not comments.
202     for (int i = 0; i < pcs.length(); i++) {
203       // Put together the reloc info
204       RelocInfo relocinfo(pcs[i], rmodes[i], datas[i]);
205 
206       // Indent the printing of the reloc info.
207       if (i == 0) {
208         // The first reloc info is printed after the disassembled instruction.
209         out.AddPadding(' ', kRelocInfoPosition - out.position());
210       } else {
211         // Additional reloc infos are printed on separate lines.
212         out.AddFormatted("\n");
213         out.AddPadding(' ', kRelocInfoPosition);
214       }
215 
216       RelocInfo::Mode rmode = relocinfo.rmode();
217       if (RelocInfo::IsPosition(rmode)) {
218         if (RelocInfo::IsStatementPosition(rmode)) {
219           out.AddFormatted("    ;; debug: statement %d", relocinfo.data());
220         } else {
221           out.AddFormatted("    ;; debug: position %d", relocinfo.data());
222         }
223       } else if (rmode == RelocInfo::EMBEDDED_OBJECT) {
224         HeapStringAllocator allocator;
225         StringStream accumulator(&allocator);
226         relocinfo.target_object()->ShortPrint(&accumulator);
227         SmartPointer<const char> obj_name = accumulator.ToCString();
228         out.AddFormatted("    ;; object: %s", *obj_name);
229       } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
230         const char* reference_name =
231             ref_encoder.NameOfAddress(*relocinfo.target_reference_address());
232         out.AddFormatted("    ;; external reference (%s)", reference_name);
233       } else if (RelocInfo::IsCodeTarget(rmode)) {
234         out.AddFormatted("    ;; code:");
235         if (rmode == RelocInfo::CONSTRUCT_CALL) {
236           out.AddFormatted(" constructor,");
237         }
238         Code* code = Code::GetCodeFromTargetAddress(relocinfo.target_address());
239         Code::Kind kind = code->kind();
240         if (code->is_inline_cache_stub()) {
241           if (rmode == RelocInfo::CODE_TARGET_CONTEXT) {
242             out.AddFormatted(" contextual,");
243           }
244           InlineCacheState ic_state = code->ic_state();
245           out.AddFormatted(" %s, %s", Code::Kind2String(kind),
246               Code::ICState2String(ic_state));
247           if (ic_state == MONOMORPHIC) {
248             PropertyType type = code->type();
249             out.AddFormatted(", %s", Code::PropertyType2String(type));
250           }
251           if (code->ic_in_loop() == IN_LOOP) {
252             out.AddFormatted(", in_loop");
253           }
254           if (kind == Code::CALL_IC || kind == Code::KEYED_CALL_IC) {
255             out.AddFormatted(", argc = %d", code->arguments_count());
256           }
257         } else if (kind == Code::STUB) {
258           // Reverse lookup required as the minor key cannot be retrieved
259           // from the code object.
260           Object* obj = heap->code_stubs()->SlowReverseLookup(code);
261           if (obj != heap->undefined_value()) {
262             ASSERT(obj->IsSmi());
263             // Get the STUB key and extract major and minor key.
264             uint32_t key = Smi::cast(obj)->value();
265             uint32_t minor_key = CodeStub::MinorKeyFromKey(key);
266             CodeStub::Major major_key = CodeStub::GetMajorKey(code);
267             ASSERT(major_key == CodeStub::MajorKeyFromKey(key));
268             out.AddFormatted(" %s, %s, ",
269                              Code::Kind2String(kind),
270                              CodeStub::MajorName(major_key, false));
271             switch (major_key) {
272               case CodeStub::CallFunction: {
273                 int argc =
274                     CallFunctionStub::ExtractArgcFromMinorKey(minor_key);
275                 out.AddFormatted("argc = %d", argc);
276                 break;
277               }
278               default:
279                 out.AddFormatted("minor: %d", minor_key);
280             }
281           }
282         } else {
283           out.AddFormatted(" %s", Code::Kind2String(kind));
284         }
285       } else if (rmode == RelocInfo::RUNTIME_ENTRY &&
286                  Isolate::Current()->deoptimizer_data() != NULL) {
287         // A runtime entry reloinfo might be a deoptimization bailout.
288         Address addr = relocinfo.target_address();
289         int id = Deoptimizer::GetDeoptimizationId(addr, Deoptimizer::EAGER);
290         if (id == Deoptimizer::kNotDeoptimizationEntry) {
291           out.AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
292         } else {
293           out.AddFormatted("    ;; deoptimization bailout %d", id);
294         }
295       } else {
296         out.AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
297       }
298     }
299     out.AddString("\n");
300     DumpBuffer(f, out.Finalize());
301     out.Reset();
302   }
303 
304   delete it;
305   return static_cast<int>(pc - begin);
306 }
307 
308 
Decode(FILE * f,byte * begin,byte * end)309 int Disassembler::Decode(FILE* f, byte* begin, byte* end) {
310   V8NameConverter defaultConverter(NULL);
311   return DecodeIt(f, defaultConverter, begin, end);
312 }
313 
314 
315 // Called by Code::CodePrint.
Decode(FILE * f,Code * code)316 void Disassembler::Decode(FILE* f, Code* code) {
317   int decode_size = (code->kind() == Code::OPTIMIZED_FUNCTION)
318       ? static_cast<int>(code->safepoint_table_offset())
319       : code->instruction_size();
320   // If there might be a stack check table, stop before reaching it.
321   if (code->kind() == Code::FUNCTION) {
322     decode_size =
323         Min(decode_size, static_cast<int>(code->stack_check_table_offset()));
324   }
325 
326   byte* begin = code->instruction_start();
327   byte* end = begin + decode_size;
328   V8NameConverter v8NameConverter(code);
329   DecodeIt(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(FILE* f, byte* begin, byte* end) { return 0; }
336 void Disassembler::Decode(FILE* f, Code* code) {}
337 
338 #endif  // ENABLE_DISASSEMBLER
339 
340 } }  // namespace v8::internal
341