disassembler.cc 16.4 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/debug/debug.h"
14 15
#include "src/deoptimizer.h"
#include "src/disasm.h"
16
#include "src/ic/ic.h"
17
#include "src/isolate-data.h"
18
#include "src/macro-assembler.h"
19
#include "src/objects-inl.h"
20
#include "src/snapshot/embedded-data.h"
21
#include "src/snapshot/serializer-common.h"
22
#include "src/string-stream.h"
23
#include "src/wasm/wasm-code-manager.h"
24
#include "src/wasm/wasm-engine.h"
25

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

#ifdef ENABLE_DISASSEMBLER

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

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

41
 private:
42 43
  void InitExternalRefsCache() const;

44 45
  Isolate* isolate_;
  CodeReference code_;
46 47

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

  // 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_;
54 55
};

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

  base::AddressRegion addressable_region =
      isolate_->root_register_addressable_region();
63
  Address isolate_root = isolate_->isolate_root();
64

65
  for (uint32_t i = 0; i < ExternalReferenceTable::kSize; i++) {
66 67
    Address address = external_reference_table->address(i);
    if (addressable_region.contains(address)) {
68
      int offset = static_cast<int>(address - isolate_root);
69 70 71 72 73
      const char* name = external_reference_table->name(i);
      directly_accessed_external_refs_.insert({offset, name});
    }
  }
}
74 75

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

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

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

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

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


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

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

118 119 120
  const int kRootsTableStart = IsolateData::roots_table_offset();
  const unsigned kRootsTableSize = sizeof(RootsTable);
  const int kExtRefsTableStart = IsolateData::external_reference_table_offset();
121
  const unsigned kExtRefsTableSize = ExternalReferenceTable::kSizeInBytes;
122 123
  const int kBuiltinsTableStart = IsolateData::builtins_table_offset();
  const unsigned kBuiltinsTableSize = Builtins::builtin_count * kPointerSize;
124

125 126
  if (static_cast<unsigned>(offset - kRootsTableStart) < kRootsTableSize) {
    uint32_t offset_in_roots_table = offset - kRootsTableStart;
127 128 129 130

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

131 132
    RootIndex root_index =
        static_cast<RootIndex>(offset_in_roots_table / kPointerSize);
133

134
    SNPrintF(v8_buffer_, "root (%s)", RootsTable::name(root_index));
135
    return v8_buffer_.start();
136

137 138 139
  } else if (static_cast<unsigned>(offset - kExtRefsTableStart) <
             kExtRefsTableSize) {
    uint32_t offset_in_extref_table = offset - kExtRefsTableStart;
140 141

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

    // Likewise if the external reference table is uninitialized.
147
    if (!isolate_->external_reference_table()->is_initialized()) {
148 149 150 151
      return nullptr;
    }

    SNPrintF(v8_buffer_, "external reference (%s)",
152
             isolate_->external_reference_table()->NameFromOffset(
153 154
                 offset_in_extref_table));
    return v8_buffer_.start();
155

156 157 158
  } else if (static_cast<unsigned>(offset - kBuiltinsTableStart) <
             kBuiltinsTableSize) {
    uint32_t offset_in_builtins_table = (offset - kBuiltinsTableStart);
159 160 161 162 163 164 165 166

    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();

167
  } else {
168 169 170 171 172 173 174 175 176 177 178
    // 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???";
179 180
  }
}
181

182 183
static void DumpBuffer(std::ostream* os, StringBuilder* out) {
  (*os) << out->Finalize() << std::endl;
184
  out->Reset();
185 186
}

187

188
static const int kOutBufferSize = 2048 + String::kMaxShortPrintLength;
189 190
static const int kRelocInfoPosition = 57;

191
static void PrintRelocInfo(StringBuilder* out, Isolate* isolate,
192
                           const ExternalReferenceEncoder* ref_encoder,
193 194
                           std::ostream* os, CodeReference host,
                           RelocInfo* relocinfo, bool first_reloc_info = true) {
195 196 197 198 199 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
  // 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) {
226 227 228 229
    const char* reference_name =
        ref_encoder ? ref_encoder->NameOfAddress(
                          isolate, relocinfo->target_external_reference())
                    : "unknown";
230
    out->AddFormatted("    ;; external reference (%s)", reference_name);
231
  } else if (RelocInfo::IsCodeTargetMode(rmode)) {
232
    out->AddFormatted("    ;; code:");
233
    Code code = isolate->heap()->GcSafeFindCodeForInnerPointer(
234
        relocinfo->target_address());
235
    Code::Kind kind = code->kind();
236
    if (code->is_builtin()) {
237
      out->AddFormatted(" Builtin::%s", Builtins::name(code->builtin_index()));
238
    } else {
239
      out->AddFormatted(" %s", Code::Kind2String(kind));
240
    }
241 242
  } else if (RelocInfo::IsWasmStubCall(rmode) && !isolate) {
    // Host is isolate-independent, try wasm native module instead.
243 244 245
    wasm::WasmCode* code = host.as_wasm_code()->native_module()->Lookup(
        relocinfo->wasm_stub_call_address());
    out->AddFormatted("    ;; wasm stub: %s", code->GetRuntimeStubName());
246
  } else if (RelocInfo::IsRuntimeEntry(rmode) && isolate &&
247
             isolate->deoptimizer_data() != nullptr) {
248
    // A runtime entry relocinfo might be a deoptimization bailout.
249
    Address addr = relocinfo->target_address();
250 251 252 253 254
    DeoptimizeKind type;
    if (Deoptimizer::IsDeoptimizationEntry(isolate, addr, &type)) {
      int id = relocinfo->GetDeoptimizationId(isolate, type);
      out->AddFormatted("    ;; %s deoptimization bailout %d",
                        Deoptimizer::MessageFor(type), id);
255
    } else {
256
      out->AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
257 258 259 260 261 262
    }
  } else {
    out->AddFormatted("    ;; %s", RelocInfo::RelocModeName(rmode));
  }
}

263
static int DecodeIt(Isolate* isolate, ExternalReferenceEncoder* ref_encoder,
264 265 266
                    std::ostream* os, CodeReference code,
                    const V8NameConverter& converter, byte* begin, byte* end,
                    Address current_pc) {
267 268
  v8::internal::EmbeddedVector<char, 128> decode_buffer;
  v8::internal::EmbeddedVector<char, kOutBufferSize> out_buffer;
269
  StringBuilder out(out_buffer.start(), out_buffer.length());
270
  byte* pc = begin;
271 272
  disasm::Disassembler d(converter,
                         disasm::Disassembler::kContinueOnUnimplementedOpcode);
273
  RelocIterator* it = nullptr;
274
  if (!code.is_null()) {
275
    it = new RelocIterator(code);
276 277 278
  } else {
    // No relocation information when printing code stubs.
  }
279
  int constants = -1;  // no constants being decoded at the start
280 281 282 283

  while (pc < end) {
    // First decode instruction so that we know its length.
    byte* prev_pc = pc;
284
    if (constants > 0) {
285 286 287
      SNPrintF(decode_buffer,
               "%08x       constant",
               *reinterpret_cast<int32_t*>(pc));
288 289 290 291 292
      constants--;
      pc += 4;
    } else {
      int num_const = d.ConstantPoolSizeAt(pc);
      if (num_const >= 0) {
293
        SNPrintF(decode_buffer,
294 295
                 "%08x       constant pool begin (num_const = %d)",
                 *reinterpret_cast<int32_t*>(pc), num_const);
296 297
        constants = num_const;
        pc += 4;
298 299
      } else if (it != nullptr && !it->done() &&
                 it->rinfo()->pc() == reinterpret_cast<Address>(pc) &&
300
                 it->rinfo()->rmode() == RelocInfo::INTERNAL_REFERENCE) {
301 302
        // raw pointer embedded in code stream, e.g., jump table
        byte* ptr = *reinterpret_cast<byte**>(pc);
jfb's avatar
jfb committed
303 304 305
        SNPrintF(
            decode_buffer, "%08" V8PRIxPTR "      jump table entry %4" PRIuS,
            reinterpret_cast<intptr_t>(ptr), static_cast<size_t>(ptr - begin));
306
        pc += sizeof(ptr);
307 308
      } else {
        decode_buffer[0] = '\0';
309
        pc += d.InstructionDecode(decode_buffer, pc);
310 311
      }
    }
312 313

    // Collect RelocInfo for this instruction (prev_pc .. pc-1)
314
    std::vector<const char*> comments;
315
    std::vector<Address> pcs;
316 317
    std::vector<RelocInfo::Mode> rmodes;
    std::vector<intptr_t> datas;
318
    if (it != nullptr) {
319
      while (!it->done() && it->rinfo()->pc() < reinterpret_cast<Address>(pc)) {
320 321 322 323 324 325 326 327 328 329
        if (RelocInfo::IsComment(it->rinfo()->rmode())) {
          // For comments just collect the text.
          comments.push_back(
              reinterpret_cast<const char*>(it->rinfo()->data()));
        } else {
          // For other reloc info collect all data.
          pcs.push_back(it->rinfo()->pc());
          rmodes.push_back(it->rinfo()->rmode());
          datas.push_back(it->rinfo()->data());
        }
330 331 332 333 334
        it->next();
      }
    }

    // Comments.
335
    for (size_t i = 0; i < comments.size(); i++) {
336
      out.AddFormatted("                  %s", comments[i]);
337
      DumpBuffer(os, &out);
338 339 340
    }

    // Instruction address and instruction offset.
341
    if (FLAG_log_colour && reinterpret_cast<Address>(prev_pc) == current_pc) {
342 343 344
      // If this is the given "current" pc, make it yellow and bold.
      out.AddFormatted("\033[33;1m");
    }
345
    out.AddFormatted("%p  %4" V8PRIxPTRDIFF "  ", static_cast<void*>(prev_pc),
346
                     prev_pc - begin);
347

348
    // Instruction.
349
    out.AddFormatted("%s", decode_buffer.start());
350 351

    // Print all the reloc info for this instruction which are not comments.
352
    for (size_t i = 0; i < pcs.size(); i++) {
353
      // Put together the reloc info
354
      const CodeReference& host = code;
355 356
      Address constant_pool =
          host.is_null() ? kNullAddress : host.constant_pool();
357
      RelocInfo relocinfo(pcs[i], rmodes[i], datas[i], Code(), constant_pool);
358

359
      bool first_reloc_info = (i == 0);
360
      PrintRelocInfo(&out, isolate, ref_encoder, os, code, &relocinfo,
361 362
                     first_reloc_info);
    }
363

364 365 366
    // 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.
367
    if (pcs.empty() && !code.is_null()) {
368
      RelocInfo dummy_rinfo(reinterpret_cast<Address>(prev_pc), RelocInfo::NONE,
369
                            0, Code());
370
      if (dummy_rinfo.IsInConstantPool()) {
371
        Address constant_pool_entry_address =
372
            dummy_rinfo.constant_pool_entry_address();
373
        RelocIterator reloc_it(code);
374 375 376
        while (!reloc_it.done()) {
          if (reloc_it.rinfo()->IsInConstantPool() &&
              (reloc_it.rinfo()->constant_pool_entry_address() ==
377
               constant_pool_entry_address)) {
378 379
            PrintRelocInfo(&out, isolate, ref_encoder, os, code,
                           reloc_it.rinfo());
380
            break;
381
          }
382
          reloc_it.next();
383 384 385
        }
      }
    }
386

387
    if (FLAG_log_colour && reinterpret_cast<Address>(prev_pc) == current_pc) {
388 389 390
      out.AddFormatted("\033[m");
    }

391
    DumpBuffer(os, &out);
392 393
  }

394
  // Emit comments following the last instruction (if any).
395 396 397 398 399 400 401 402
  if (it != nullptr) {
    for ( ; !it->done(); it->next()) {
      if (RelocInfo::IsComment(it->rinfo()->rmode())) {
        out.AddFormatted("                  %s",
                         reinterpret_cast<const char*>(it->rinfo()->data()));
        DumpBuffer(os, &out);
      }
    }
403 404
  }

405
  delete it;
406
  return static_cast<int>(pc - begin);
407 408
}

409
int Disassembler::Decode(Isolate* isolate, std::ostream* os, byte* begin,
410
                         byte* end, CodeReference code, Address current_pc) {
411
  V8NameConverter v8NameConverter(isolate, code);
412 413 414
  bool decode_off_heap = isolate && InstructionStream::PcIsOffHeap(
                                        isolate, bit_cast<Address>(begin));
  CodeReference code_ref = decode_off_heap ? CodeReference() : code;
415 416 417 418 419
  if (isolate) {
    // We have an isolate, so support external reference names.
    SealHandleScope shs(isolate);
    DisallowHeapAllocation no_alloc;
    ExternalReferenceEncoder ref_encoder(isolate);
420
    return DecodeIt(isolate, &ref_encoder, os, code_ref, v8NameConverter, begin,
421
                    end, current_pc);
422 423
  } else {
    // No isolate => isolate-independent code. No external reference names.
424
    return DecodeIt(nullptr, nullptr, os, code_ref, v8NameConverter, begin, end,
425 426
                    current_pc);
  }
427 428
}

429 430
#else  // ENABLE_DISASSEMBLER

431
int Disassembler::Decode(Isolate* isolate, std::ostream* os, byte* begin,
432
                         byte* end, CodeReference code, Address current_pc) {
433 434
  return 0;
}
435

436 437
#endif  // ENABLE_DISASSEMBLER

438 439
}  // namespace internal
}  // namespace v8