disassembler.cc 16.9 KB
Newer Older
1
// Copyright 2011 the V8 project authors. All rights reserved.
2 3
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
4

5
#include "src/disassembler.h"
6

7
#include <memory>
8
#include <unordered_map>
9
#include <vector>
10

11
#include "src/assembler-inl.h"
12
#include "src/code-reference.h"
13
#include "src/code-stubs.h"
14
#include "src/debug/debug.h"
15 16
#include "src/deoptimizer.h"
#include "src/disasm.h"
17
#include "src/ic/ic.h"
18
#include "src/instruction-stream.h"
19
#include "src/isolate-data.h"
20
#include "src/macro-assembler.h"
21
#include "src/objects-inl.h"
22
#include "src/snapshot/serializer-common.h"
23
#include "src/string-stream.h"
24
#include "src/wasm/wasm-code-manager.h"
25
#include "src/wasm/wasm-engine.h"
26

27 28
namespace v8 {
namespace internal {
29 30 31 32 33

#ifdef ENABLE_DISASSEMBLER

class V8NameConverter: public disasm::NameConverter {
 public:
34 35
  explicit V8NameConverter(Isolate* isolate, CodeReference code = {})
      : isolate_(isolate), code_(code) {}
36 37 38 39
  const char* NameOfAddress(byte* pc) const override;
  const char* NameInCode(byte* addr) const override;
  const char* RootRelativeName(int offset) const override;

40 41
  const CodeReference& code() const { return code_; }

42
 private:
43 44
  void InitExternalRefsCache() const;

45 46
  Isolate* isolate_;
  CodeReference code_;
47 48

  EmbeddedVector<char, 128> v8_buffer_;
49 50 51 52 53 54

  // Map from root-register relative offset of the external reference value to
  // the external reference name (stored in the external reference table).
  // This cache is used to recognize [root_reg + offs] patterns as direct
  // access to certain external reference's value.
  mutable std::unordered_map<int, const char*> directly_accessed_external_refs_;
55 56
};

57 58
void V8NameConverter::InitExternalRefsCache() const {
  ExternalReferenceTable* external_reference_table =
59
      isolate_->external_reference_table();
60 61 62 63 64
  if (!external_reference_table->is_initialized()) return;

  base::AddressRegion addressable_region =
      isolate_->root_register_addressable_region();
  Address roots_start =
65
      reinterpret_cast<Address>(isolate_->roots_array_start());
66 67 68 69 70 71 72 73 74 75

  for (uint32_t i = 0; i < external_reference_table->size(); i++) {
    Address address = external_reference_table->address(i);
    if (addressable_region.contains(address)) {
      int offset = static_cast<int>(address - roots_start);
      const char* name = external_reference_table->name(i);
      directly_accessed_external_refs_.insert({offset, name});
    }
  }
}
76 77

const char* V8NameConverter::NameOfAddress(byte* pc) const {
78
  if (!code_.is_null()) {
79
    const char* name =
80 81
        isolate_ ? isolate_->builtins()->Lookup(reinterpret_cast<Address>(pc))
                 : nullptr;
82

83 84 85 86
    if (name != nullptr) {
      SNPrintF(v8_buffer_, "%p  (%s)", static_cast<void*>(pc), name);
      return v8_buffer_.start();
    }
87

88
    int offs = static_cast<int>(reinterpret_cast<Address>(pc) -
89
                                code_.instruction_start());
90
    // print as code offset, if it seems reasonable
91
    if (0 <= offs && offs < code_.instruction_size()) {
92
      SNPrintF(v8_buffer_, "%p  <+0x%x>", static_cast<void*>(pc), offs);
93
      return v8_buffer_.start();
94
    }
95

96
    wasm::WasmCode* wasm_code =
97 98 99
        isolate_ ? isolate_->wasm_engine()->code_manager()->LookupCode(
                       reinterpret_cast<Address>(pc))
                 : nullptr;
100 101
    if (wasm_code != nullptr) {
      SNPrintF(v8_buffer_, "%p  (%s)", static_cast<void*>(pc),
102
               wasm::GetWasmCodeKindAsString(wasm_code->kind()));
103 104
      return v8_buffer_.start();
    }
105 106 107 108 109 110
  }

  return disasm::NameConverter::NameOfAddress(pc);
}


111 112 113
const char* V8NameConverter::NameInCode(byte* addr) const {
  // The V8NameConverter is used for well known code, so we can "safely"
  // dereference pointers in generated code.
114
  return code_.is_null() ? "" : reinterpret_cast<const char*>(addr);
115 116
}

117 118 119
const char* V8NameConverter::RootRelativeName(int offset) const {
  if (isolate_ == nullptr) return nullptr;

120 121 122 123 124 125
  const int kRootsStart = IsolateData::kRootsTableOffset;
  const int kRootsEnd = IsolateData::kRootsTableEndOffset;
  const int kExtRefsStart = IsolateData::kExternalReferenceTableOffset;
  const int kExtRefsEnd = IsolateData::kExternalReferenceTableEndOffset;
  const int kBuiltinsStart = IsolateData::kBuiltinsTableOffset;
  const int kBuiltinsEnd = IsolateData::kBuiltinsTableEndOffset;
126 127 128 129 130 131 132

  if (kRootsStart <= offset && offset < kRootsEnd) {
    uint32_t offset_in_roots_table = offset - kRootsStart;

    // Fail safe in the unlikely case of an arbitrary root-relative offset.
    if (offset_in_roots_table % kPointerSize != 0) return nullptr;

133 134
    RootIndex root_index =
        static_cast<RootIndex>(offset_in_roots_table / kPointerSize);
135 136 137

    HeapStringAllocator allocator;
    StringStream accumulator(&allocator);
138
    isolate_->root(root_index)->ShortPrint(&accumulator);
139 140 141 142
    std::unique_ptr<char[]> obj_name = accumulator.ToCString();

    SNPrintF(v8_buffer_, "root (%s)", obj_name.get());
    return v8_buffer_.start();
143

144 145 146 147 148 149 150 151 152
  } else if (kExtRefsStart <= offset && offset < kExtRefsEnd) {
    uint32_t offset_in_extref_table = offset - kExtRefsStart;

    // Fail safe in the unlikely case of an arbitrary root-relative offset.
    if (offset_in_extref_table % ExternalReferenceTable::EntrySize() != 0) {
      return nullptr;
    }

    // Likewise if the external reference table is uninitialized.
153
    if (!isolate_->external_reference_table()->is_initialized()) {
154 155 156 157
      return nullptr;
    }

    SNPrintF(v8_buffer_, "external reference (%s)",
158
             isolate_->external_reference_table()->NameFromOffset(
159 160
                 offset_in_extref_table));
    return v8_buffer_.start();
161 162 163 164 165 166 167 168 169 170 171

  } else if (kBuiltinsStart <= offset && offset < kBuiltinsEnd) {
    uint32_t offset_in_builtins_table = (offset - kBuiltinsStart);

    Builtins::Name builtin_id =
        static_cast<Builtins::Name>(offset_in_builtins_table / kPointerSize);

    const char* name = Builtins::name(builtin_id);
    SNPrintF(v8_buffer_, "builtin (%s)", name);
    return v8_buffer_.start();

172
  } else {
173 174 175 176 177 178 179 180 181 182 183
    // It must be a direct access to one of the external values.
    if (directly_accessed_external_refs_.empty()) {
      InitExternalRefsCache();
    }

    auto iter = directly_accessed_external_refs_.find(offset);
    if (iter != directly_accessed_external_refs_.end()) {
      SNPrintF(v8_buffer_, "external value (%s)", iter->second);
      return v8_buffer_.start();
    }
    return "WAAT??? What are we accessing here???";
184 185
  }
}
186

187 188
static void DumpBuffer(std::ostream* os, StringBuilder* out) {
  (*os) << out->Finalize() << std::endl;
189
  out->Reset();
190 191
}

192

193
static const int kOutBufferSize = 2048 + String::kMaxShortPrintLength;
194 195
static const int kRelocInfoPosition = 57;

196
static void PrintRelocInfo(StringBuilder* out, Isolate* isolate,
197
                           const ExternalReferenceEncoder* ref_encoder,
198 199
                           std::ostream* os, CodeReference host,
                           RelocInfo* relocinfo, bool first_reloc_info = true) {
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
  // Indent the printing of the reloc info.
  if (first_reloc_info) {
    // The first reloc info is printed after the disassembled instruction.
    out->AddPadding(' ', kRelocInfoPosition - out->position());
  } else {
    // Additional reloc infos are printed on separate lines.
    DumpBuffer(os, out);
    out->AddPadding(' ', kRelocInfoPosition);
  }

  RelocInfo::Mode rmode = relocinfo->rmode();
  if (rmode == RelocInfo::DEOPT_SCRIPT_OFFSET) {
    out->AddFormatted("    ;; debug: deopt position, script offset '%d'",
                      static_cast<int>(relocinfo->data()));
  } else if (rmode == RelocInfo::DEOPT_INLINING_ID) {
    out->AddFormatted("    ;; debug: deopt position, inlining id '%d'",
                      static_cast<int>(relocinfo->data()));
  } else if (rmode == RelocInfo::DEOPT_REASON) {
    DeoptimizeReason reason = static_cast<DeoptimizeReason>(relocinfo->data());
    out->AddFormatted("    ;; debug: deopt reason '%s'",
                      DeoptimizeReasonToString(reason));
  } else if (rmode == RelocInfo::DEOPT_ID) {
    out->AddFormatted("    ;; debug: deopt index %d",
                      static_cast<int>(relocinfo->data()));
  } else if (rmode == RelocInfo::EMBEDDED_OBJECT) {
    HeapStringAllocator allocator;
    StringStream accumulator(&allocator);
    relocinfo->target_object()->ShortPrint(&accumulator);
    std::unique_ptr<char[]> obj_name = accumulator.ToCString();
    out->AddFormatted("    ;; object: %s", obj_name.get());
  } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
231 232 233 234
    const char* reference_name =
        ref_encoder ? ref_encoder->NameOfAddress(
                          isolate, relocinfo->target_external_reference())
                    : "unknown";
235
    out->AddFormatted("    ;; external reference (%s)", reference_name);
236
  } else if (RelocInfo::IsCodeTargetMode(rmode)) {
237
    out->AddFormatted("    ;; code:");
238 239
    Code* code = isolate->heap()->GcSafeFindCodeForInnerPointer(
        relocinfo->target_address());
240 241 242 243 244 245 246 247 248 249 250 251
    Code::Kind kind = code->kind();
    if (kind == Code::STUB) {
      // Get the STUB key and extract major and minor key.
      uint32_t key = code->stub_key();
      uint32_t minor_key = CodeStub::MinorKeyFromKey(key);
      CodeStub::Major major_key = CodeStub::GetMajorKey(code);
      DCHECK(major_key == CodeStub::MajorKeyFromKey(key));
      out->AddFormatted(" %s, %s, ", Code::Kind2String(kind),
                        CodeStub::MajorName(major_key));
      out->AddFormatted("minor: %d", minor_key);
    } else if (code->is_builtin()) {
      out->AddFormatted(" Builtin::%s", Builtins::name(code->builtin_index()));
252
    } else {
253
      out->AddFormatted(" %s", Code::Kind2String(kind));
254
    }
255 256
  } else if (RelocInfo::IsWasmStubCall(rmode) && !isolate) {
    // Host is isolate-independent, try wasm native module instead.
257 258 259
    wasm::WasmCode* code = host.as_wasm_code()->native_module()->Lookup(
        relocinfo->wasm_stub_call_address());
    out->AddFormatted("    ;; wasm stub: %s", code->GetRuntimeStubName());
260
  } else if (RelocInfo::IsRuntimeEntry(rmode) && isolate &&
261
             isolate->deoptimizer_data() != nullptr) {
262
    // A runtime entry relocinfo might be a deoptimization bailout.
263
    Address addr = relocinfo->target_address();
264 265 266 267 268
    DeoptimizeKind type;
    if (Deoptimizer::IsDeoptimizationEntry(isolate, addr, &type)) {
      int id = relocinfo->GetDeoptimizationId(isolate, type);
      out->AddFormatted("    ;; %s deoptimization bailout %d",
                        Deoptimizer::MessageFor(type), id);
269
    } else {
270
      out->AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
271 272 273 274 275 276
    }
  } else {
    out->AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
  }
}

277
static int DecodeIt(Isolate* isolate, ExternalReferenceEncoder* ref_encoder,
278 279 280
                    std::ostream* os, CodeReference code,
                    const V8NameConverter& converter, byte* begin, byte* end,
                    Address current_pc) {
281 282
  v8::internal::EmbeddedVector<char, 128> decode_buffer;
  v8::internal::EmbeddedVector<char, kOutBufferSize> out_buffer;
283
  StringBuilder out(out_buffer.start(), out_buffer.length());
284
  byte* pc = begin;
285 286
  disasm::Disassembler d(converter,
                         disasm::Disassembler::kContinueOnUnimplementedOpcode);
287
  RelocIterator* it = nullptr;
288 289
  if (!code.is_null()) {
    it = new RelocIterator(code);
290 291 292
  } else {
    // No relocation information when printing code stubs.
  }
293
  int constants = -1;  // no constants being decoded at the start
294 295 296 297

  while (pc < end) {
    // First decode instruction so that we know its length.
    byte* prev_pc = pc;
298
    if (constants > 0) {
299 300 301
      SNPrintF(decode_buffer,
               "%08x       constant",
               *reinterpret_cast<int32_t*>(pc));
302 303 304 305 306
      constants--;
      pc += 4;
    } else {
      int num_const = d.ConstantPoolSizeAt(pc);
      if (num_const >= 0) {
307
        SNPrintF(decode_buffer,
308 309
                 "%08x       constant pool begin (num_const = %d)",
                 *reinterpret_cast<int32_t*>(pc), num_const);
310 311
        constants = num_const;
        pc += 4;
312 313
      } else if (it != nullptr && !it->done() &&
                 it->rinfo()->pc() == reinterpret_cast<Address>(pc) &&
314
                 it->rinfo()->rmode() == RelocInfo::INTERNAL_REFERENCE) {
315 316
        // raw pointer embedded in code stream, e.g., jump table
        byte* ptr = *reinterpret_cast<byte**>(pc);
jfb's avatar
jfb committed
317 318 319
        SNPrintF(
            decode_buffer, "%08" V8PRIxPTR "      jump table entry %4" PRIuS,
            reinterpret_cast<intptr_t>(ptr), static_cast<size_t>(ptr - begin));
320
        pc += sizeof(ptr);
321 322
      } else {
        decode_buffer[0] = '\0';
323
        pc += d.InstructionDecode(decode_buffer, pc);
324 325
      }
    }
326 327

    // Collect RelocInfo for this instruction (prev_pc .. pc-1)
328
    std::vector<const char*> comments;
329
    std::vector<Address> pcs;
330 331
    std::vector<RelocInfo::Mode> rmodes;
    std::vector<intptr_t> datas;
332
    if (it != nullptr) {
333
      while (!it->done() && it->rinfo()->pc() < reinterpret_cast<Address>(pc)) {
334
        if (RelocInfo::IsComment(it->rinfo()->rmode())) {
335
          // For comments just collect the text.
336 337
          comments.push_back(
              reinterpret_cast<const char*>(it->rinfo()->data()));
338 339
        } else {
          // For other reloc info collect all data.
340 341 342
          pcs.push_back(it->rinfo()->pc());
          rmodes.push_back(it->rinfo()->rmode());
          datas.push_back(it->rinfo()->data());
343 344 345 346 347 348
        }
        it->next();
      }
    }

    // Comments.
349
    for (size_t i = 0; i < comments.size(); i++) {
350
      out.AddFormatted("                  %s", comments[i]);
351
      DumpBuffer(os, &out);
352 353 354
    }

    // Instruction address and instruction offset.
355
    if (FLAG_log_colour && reinterpret_cast<Address>(prev_pc) == current_pc) {
356 357 358
      // If this is the given "current" pc, make it yellow and bold.
      out.AddFormatted("\033[33;1m");
    }
359
    out.AddFormatted("%p  %4" V8PRIxPTRDIFF "  ", static_cast<void*>(prev_pc),
360
                     prev_pc - begin);
361

362
    // Instruction.
363
    out.AddFormatted("%s", decode_buffer.start());
364 365

    // Print all the reloc info for this instruction which are not comments.
366
    for (size_t i = 0; i < pcs.size(); i++) {
367
      // Put together the reloc info
368
      const CodeReference& host = code;
369 370 371
      Address constant_pool =
          host.is_null() ? kNullAddress : host.constant_pool();
      RelocInfo relocinfo(pcs[i], rmodes[i], datas[i], nullptr, constant_pool);
372

373
      bool first_reloc_info = (i == 0);
374
      PrintRelocInfo(&out, isolate, ref_encoder, os, code, &relocinfo,
375 376
                     first_reloc_info);
    }
377

378 379 380
    // If this is a constant pool load and we haven't found any RelocInfo
    // already, check if we can find some RelocInfo for the target address in
    // the constant pool.
381
    if (pcs.empty() && !code.is_null()) {
382 383
      RelocInfo dummy_rinfo(reinterpret_cast<Address>(prev_pc), RelocInfo::NONE,
                            0, nullptr);
384
      if (dummy_rinfo.IsInConstantPool()) {
385
        Address constant_pool_entry_address =
386
            dummy_rinfo.constant_pool_entry_address();
387
        RelocIterator reloc_it(code);
388 389 390
        while (!reloc_it.done()) {
          if (reloc_it.rinfo()->IsInConstantPool() &&
              (reloc_it.rinfo()->constant_pool_entry_address() ==
391
               constant_pool_entry_address)) {
392 393
            PrintRelocInfo(&out, isolate, ref_encoder, os, code,
                           reloc_it.rinfo());
394
            break;
395
          }
396
          reloc_it.next();
397 398 399
        }
      }
    }
400

401
    if (FLAG_log_colour && reinterpret_cast<Address>(prev_pc) == current_pc) {
402 403 404
      out.AddFormatted("\033[m");
    }

405
    DumpBuffer(os, &out);
406 407
  }

408
  // Emit comments following the last instruction (if any).
409
  if (it != nullptr) {
410 411 412 413
    for ( ; !it->done(); it->next()) {
      if (RelocInfo::IsComment(it->rinfo()->rmode())) {
        out.AddFormatted("                  %s",
                         reinterpret_cast<const char*>(it->rinfo()->data()));
414
        DumpBuffer(os, &out);
415 416 417 418
      }
    }
  }

419
  delete it;
420
  return static_cast<int>(pc - begin);
421 422
}

423
int Disassembler::Decode(Isolate* isolate, std::ostream* os, byte* begin,
424
                         byte* end, CodeReference code, Address current_pc) {
425
  V8NameConverter v8NameConverter(isolate, code);
426 427 428
  bool decode_off_heap = isolate && InstructionStream::PcIsOffHeap(
                                        isolate, bit_cast<Address>(begin));
  CodeReference code_ref = decode_off_heap ? CodeReference() : code;
429 430 431 432 433
  if (isolate) {
    // We have an isolate, so support external reference names.
    SealHandleScope shs(isolate);
    DisallowHeapAllocation no_alloc;
    ExternalReferenceEncoder ref_encoder(isolate);
434 435
    return DecodeIt(isolate, &ref_encoder, os, code_ref, v8NameConverter, begin,
                    end, current_pc);
436 437
  } else {
    // No isolate => isolate-independent code. No external reference names.
438
    return DecodeIt(nullptr, nullptr, os, code_ref, v8NameConverter, begin, end,
439 440
                    current_pc);
  }
441 442
}

443 444
#else  // ENABLE_DISASSEMBLER

445
int Disassembler::Decode(Isolate* isolate, std::ostream* os, byte* begin,
446
                         byte* end, CodeReference code, Address current_pc) {
447 448
  return 0;
}
449

450 451
#endif  // ENABLE_DISASSEMBLER

452 453
}  // namespace internal
}  // namespace v8