mirror of
https://github.com/dart-lang/sdk
synced 2024-09-19 21:11:40 +00:00
add88f4ee3
When printing function objects to JSON, we still allocate temporary strings on the Dart heap. BUG= Review URL: https://codereview.chromium.org/2372973003 .
331 lines
12 KiB
C++
331 lines
12 KiB
C++
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
|
|
// for details. All rights reserved. Use of this source code is governed by a
|
|
// BSD-style license that can be found in the LICENSE file.
|
|
|
|
#include "vm/disassembler.h"
|
|
|
|
#include "vm/assembler.h"
|
|
#include "vm/deopt_instructions.h"
|
|
#include "vm/globals.h"
|
|
#include "vm/il_printer.h"
|
|
#include "vm/instructions.h"
|
|
#include "vm/json_stream.h"
|
|
#include "vm/log.h"
|
|
#include "vm/os.h"
|
|
#include "vm/code_patcher.h"
|
|
|
|
|
|
namespace dart {
|
|
|
|
#ifndef PRODUCT
|
|
|
|
DECLARE_FLAG(bool, trace_inlining_intervals);
|
|
|
|
void DisassembleToStdout::ConsumeInstruction(const Code& code,
|
|
char* hex_buffer,
|
|
intptr_t hex_size,
|
|
char* human_buffer,
|
|
intptr_t human_size,
|
|
Object* object,
|
|
uword pc) {
|
|
static const int kHexColumnWidth = 23;
|
|
uint8_t* pc_ptr = reinterpret_cast<uint8_t*>(pc);
|
|
THR_Print("%p %s", pc_ptr, hex_buffer);
|
|
int hex_length = strlen(hex_buffer);
|
|
if (hex_length < kHexColumnWidth) {
|
|
for (int i = kHexColumnWidth - hex_length; i > 0; i--) {
|
|
THR_Print(" ");
|
|
}
|
|
}
|
|
THR_Print("%s", human_buffer);
|
|
if (object != NULL) {
|
|
THR_Print(" %s", object->ToCString());
|
|
}
|
|
THR_Print("\n");
|
|
}
|
|
|
|
|
|
void DisassembleToStdout::Print(const char* format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
THR_VPrint(format, args);
|
|
va_end(args);
|
|
}
|
|
|
|
|
|
void DisassembleToJSONStream::ConsumeInstruction(const Code& code,
|
|
char* hex_buffer,
|
|
intptr_t hex_size,
|
|
char* human_buffer,
|
|
intptr_t human_size,
|
|
Object* object,
|
|
uword pc) {
|
|
// Instructions are represented as four consecutive values in a JSON array.
|
|
// The first is the address of the instruction, the second is the hex string,
|
|
// of the code, and the third is a human readable string, and the fourth is
|
|
// the object loaded by the instruction.
|
|
jsarr_.AddValueF("%" Pp "", pc);
|
|
jsarr_.AddValue(hex_buffer);
|
|
jsarr_.AddValue(human_buffer);
|
|
|
|
if (object != NULL) {
|
|
jsarr_.AddValue(*object);
|
|
} else {
|
|
jsarr_.AddValueNull(); // Not a reference to null.
|
|
}
|
|
}
|
|
|
|
|
|
void DisassembleToJSONStream::Print(const char* format, ...) {
|
|
va_list args;
|
|
va_start(args, format);
|
|
intptr_t len = OS::VSNPrint(NULL, 0, format, args);
|
|
va_end(args);
|
|
char* p = reinterpret_cast<char*>(malloc(len+1));
|
|
va_start(args, format);
|
|
intptr_t len2 = OS::VSNPrint(p, len, format, args);
|
|
va_end(args);
|
|
ASSERT(len == len2);
|
|
for (intptr_t i = 0; i < len; i++) {
|
|
if (p[i] == '\n' || p[i] == '\r') {
|
|
p[i] = ' ';
|
|
}
|
|
}
|
|
// Instructions are represented as four consecutive values in a JSON array.
|
|
// Comments only use the third slot. See above comment for more information.
|
|
jsarr_.AddValueNull();
|
|
jsarr_.AddValueNull();
|
|
jsarr_.AddValue(p);
|
|
jsarr_.AddValueNull();
|
|
free(p);
|
|
}
|
|
|
|
|
|
void Disassembler::Disassemble(uword start,
|
|
uword end,
|
|
DisassemblyFormatter* formatter,
|
|
const Code& code) {
|
|
const Code::Comments& comments =
|
|
code.IsNull() ? Code::Comments::New(0) : code.comments();
|
|
ASSERT(formatter != NULL);
|
|
char hex_buffer[kHexadecimalBufferSize]; // Instruction in hexadecimal form.
|
|
char human_buffer[kUserReadableBufferSize]; // Human-readable instruction.
|
|
uword pc = start;
|
|
intptr_t comment_finger = 0;
|
|
GrowableArray<Function*> inlined_functions;
|
|
while (pc < end) {
|
|
const intptr_t offset = pc - start;
|
|
const intptr_t old_comment_finger = comment_finger;
|
|
while (comment_finger < comments.Length() &&
|
|
comments.PCOffsetAt(comment_finger) <= offset) {
|
|
formatter->Print(
|
|
" ;; %s\n",
|
|
String::Handle(comments.CommentAt(comment_finger)).ToCString());
|
|
comment_finger++;
|
|
}
|
|
if (old_comment_finger != comment_finger) {
|
|
char str[4000];
|
|
BufferFormatter f(str, sizeof(str));
|
|
// Comment emitted, emit inlining information.
|
|
code.GetInlinedFunctionsAt(offset, &inlined_functions);
|
|
// Skip top scope function printing (last entry in 'inlined_functions').
|
|
bool first = true;
|
|
for (intptr_t i = inlined_functions.length() - 2; i >= 0; i--) {
|
|
const char* name = inlined_functions[i]->ToQualifiedCString();
|
|
if (first) {
|
|
f.Print(" ;; Inlined [%s", name);
|
|
first = false;
|
|
} else {
|
|
f.Print(" -> %s", name);
|
|
}
|
|
}
|
|
if (!first) {
|
|
f.Print("]\n");
|
|
formatter->Print(str);
|
|
}
|
|
}
|
|
int instruction_length;
|
|
Object* object;
|
|
DecodeInstruction(hex_buffer,
|
|
sizeof(hex_buffer),
|
|
human_buffer,
|
|
sizeof(human_buffer),
|
|
&instruction_length, code, &object, pc);
|
|
formatter->ConsumeInstruction(code,
|
|
hex_buffer,
|
|
sizeof(hex_buffer),
|
|
human_buffer,
|
|
sizeof(human_buffer),
|
|
object,
|
|
pc);
|
|
pc += instruction_length;
|
|
}
|
|
}
|
|
|
|
|
|
void Disassembler::DisassembleCodeHelper(
|
|
const char* function_fullname, const Code& code, bool optimized) {
|
|
LocalVarDescriptors& var_descriptors = LocalVarDescriptors::Handle();
|
|
if (FLAG_print_variable_descriptors) {
|
|
var_descriptors = code.GetLocalVarDescriptors();
|
|
}
|
|
THR_Print("Code for %sfunction '%s' {\n",
|
|
optimized ? "optimized " : "",
|
|
function_fullname);
|
|
code.Disassemble();
|
|
THR_Print("}\n");
|
|
|
|
#if defined(TARGET_ARCH_IA32)
|
|
THR_Print("Pointer offsets for function: {\n");
|
|
// Pointer offsets are stored in descending order.
|
|
Object& obj = Object::Handle();
|
|
for (intptr_t i = code.pointer_offsets_length() - 1; i >= 0; i--) {
|
|
const uword addr = code.GetPointerOffsetAt(i) + code.PayloadStart();
|
|
obj = *reinterpret_cast<RawObject**>(addr);
|
|
THR_Print(" %d : %#" Px " '%s'\n",
|
|
code.GetPointerOffsetAt(i), addr, obj.ToCString());
|
|
}
|
|
THR_Print("}\n");
|
|
#else
|
|
ASSERT(code.pointer_offsets_length() == 0);
|
|
#endif
|
|
|
|
const ObjectPool& object_pool = ObjectPool::Handle(code.GetObjectPool());
|
|
object_pool.DebugPrint();
|
|
|
|
THR_Print("PC Descriptors for function '%s' {\n", function_fullname);
|
|
PcDescriptors::PrintHeaderString();
|
|
const PcDescriptors& descriptors =
|
|
PcDescriptors::Handle(code.pc_descriptors());
|
|
THR_Print("%s}\n", descriptors.ToCString());
|
|
|
|
uword start = Instructions::Handle(code.instructions()).PayloadStart();
|
|
const Array& deopt_table = Array::Handle(code.deopt_info_array());
|
|
intptr_t deopt_table_length = DeoptTable::GetLength(deopt_table);
|
|
if (deopt_table_length > 0) {
|
|
THR_Print("DeoptInfo: {\n");
|
|
Smi& offset = Smi::Handle();
|
|
TypedData& info = TypedData::Handle();
|
|
Smi& reason_and_flags = Smi::Handle();
|
|
for (intptr_t i = 0; i < deopt_table_length; ++i) {
|
|
DeoptTable::GetEntry(deopt_table, i, &offset, &info, &reason_and_flags);
|
|
const intptr_t reason =
|
|
DeoptTable::ReasonField::decode(reason_and_flags.Value());
|
|
ASSERT((0 <= reason) && (reason < ICData::kDeoptNumReasons));
|
|
THR_Print("%4" Pd ": 0x%" Px " %s (%s)\n",
|
|
i,
|
|
start + offset.Value(),
|
|
DeoptInfo::ToCString(deopt_table, info),
|
|
DeoptReasonToCString(
|
|
static_cast<ICData::DeoptReasonId>(reason)));
|
|
}
|
|
THR_Print("}\n");
|
|
}
|
|
|
|
THR_Print("Stackmaps for function '%s' {\n", function_fullname);
|
|
if (code.stackmaps() != Array::null()) {
|
|
const Array& stackmap_table = Array::Handle(code.stackmaps());
|
|
Stackmap& map = Stackmap::Handle();
|
|
for (intptr_t i = 0; i < stackmap_table.Length(); ++i) {
|
|
map ^= stackmap_table.At(i);
|
|
THR_Print("%s\n", map.ToCString());
|
|
}
|
|
}
|
|
THR_Print("}\n");
|
|
|
|
if (FLAG_print_variable_descriptors) {
|
|
THR_Print("Variable Descriptors for function '%s' {\n",
|
|
function_fullname);
|
|
intptr_t var_desc_length =
|
|
var_descriptors.IsNull() ? 0 : var_descriptors.Length();
|
|
String& var_name = String::Handle();
|
|
for (intptr_t i = 0; i < var_desc_length; i++) {
|
|
var_name = var_descriptors.GetName(i);
|
|
RawLocalVarDescriptors::VarInfo var_info;
|
|
var_descriptors.GetInfo(i, &var_info);
|
|
const int8_t kind = var_info.kind();
|
|
if (kind == RawLocalVarDescriptors::kSavedCurrentContext) {
|
|
THR_Print(" saved current CTX reg offset %d\n", var_info.index());
|
|
} else {
|
|
if (kind == RawLocalVarDescriptors::kContextLevel) {
|
|
THR_Print(" context level %d scope %d", var_info.index(),
|
|
var_info.scope_id);
|
|
} else if (kind == RawLocalVarDescriptors::kStackVar) {
|
|
THR_Print(" stack var '%s' offset %d",
|
|
var_name.ToCString(), var_info.index());
|
|
} else {
|
|
ASSERT(kind == RawLocalVarDescriptors::kContextVar);
|
|
THR_Print(" context var '%s' level %d offset %d",
|
|
var_name.ToCString(), var_info.scope_id, var_info.index());
|
|
}
|
|
THR_Print(" (valid %s-%s)\n", var_info.begin_pos.ToCString(),
|
|
var_info.end_pos.ToCString());
|
|
}
|
|
}
|
|
THR_Print("}\n");
|
|
}
|
|
|
|
THR_Print("Exception Handlers for function '%s' {\n", function_fullname);
|
|
const ExceptionHandlers& handlers =
|
|
ExceptionHandlers::Handle(code.exception_handlers());
|
|
THR_Print("%s}\n", handlers.ToCString());
|
|
|
|
{
|
|
THR_Print("Static call target functions {\n");
|
|
const Array& table = Array::Handle(code.static_calls_target_table());
|
|
Smi& offset = Smi::Handle();
|
|
Function& function = Function::Handle();
|
|
Code& code = Code::Handle();
|
|
for (intptr_t i = 0; i < table.Length();
|
|
i += Code::kSCallTableEntryLength) {
|
|
offset ^= table.At(i + Code::kSCallTableOffsetEntry);
|
|
function ^= table.At(i + Code::kSCallTableFunctionEntry);
|
|
code ^= table.At(i + Code::kSCallTableCodeEntry);
|
|
if (function.IsNull()) {
|
|
Class& cls = Class::Handle();
|
|
cls ^= code.owner();
|
|
if (cls.IsNull()) {
|
|
THR_Print(" 0x%" Px ": %s, %p\n",
|
|
start + offset.Value(),
|
|
code.Name(),
|
|
code.raw());
|
|
} else {
|
|
THR_Print(" 0x%" Px ": allocation stub for %s, %p\n",
|
|
start + offset.Value(),
|
|
cls.ToCString(),
|
|
code.raw());
|
|
}
|
|
} else {
|
|
THR_Print(" 0x%" Px ": %s, %p\n",
|
|
start + offset.Value(),
|
|
function.ToFullyQualifiedCString(),
|
|
code.raw());
|
|
}
|
|
}
|
|
THR_Print("}\n");
|
|
}
|
|
if (optimized && FLAG_trace_inlining_intervals) {
|
|
code.DumpInlinedIntervals();
|
|
}
|
|
}
|
|
|
|
|
|
void Disassembler::DisassembleCode(const Function& function, bool optimized) {
|
|
const char* function_fullname = function.ToFullyQualifiedCString();
|
|
const Code& code = Code::Handle(function.CurrentCode());
|
|
DisassembleCodeHelper(function_fullname, code, optimized);
|
|
}
|
|
|
|
|
|
void Disassembler::DisassembleCodeUnoptimized(
|
|
const Function& function, bool optimized) {
|
|
const char* function_fullname = function.ToFullyQualifiedCString();
|
|
const Code& code = Code::Handle(function.unoptimized_code());
|
|
DisassembleCodeHelper(function_fullname, code, optimized);
|
|
}
|
|
|
|
|
|
#endif // !PRODUCT
|
|
|
|
} // namespace dart
|