mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 08:24:39 +00:00
[vm] Support disassembling snapshot code at runtime for JIT and AOT snapshots.
--code-comments should be passed both at compile/training-time and run-time to get comments in the disassembled instructions. The required --disassemble-* flags need only be passed at run-time. Flow-graph printing is not yet supported because we don't save flow-graph like assembly comments. This functionality is excluded from product builds. Change-Id: If35fbb08cff6eae40069f916ecec9c6f589c0cad Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/124989 Commit-Queue: Samir Jindel <sjindel@google.com> Reviewed-by: Martin Kustermann <kustermann@google.com>
This commit is contained in:
parent
3bd7ed38fb
commit
dd7760a07d
|
@ -9,9 +9,12 @@
|
|||
#include "vm/bss_relocs.h"
|
||||
#include "vm/class_id.h"
|
||||
#include "vm/code_observers.h"
|
||||
#include "vm/compiler/assembler/disassembler.h"
|
||||
#include "vm/compiler/backend/code_statistics.h"
|
||||
#include "vm/compiler/backend/il_printer.h"
|
||||
#include "vm/compiler/relocation.h"
|
||||
#include "vm/dart.h"
|
||||
#include "vm/flag_list.h"
|
||||
#include "vm/heap/heap.h"
|
||||
#include "vm/image_snapshot.h"
|
||||
#include "vm/native_entry.h"
|
||||
|
@ -1387,7 +1390,12 @@ class CodeSerializationCluster : public SerializationCluster {
|
|||
s->Push(code->ptr()->deopt_info_array_);
|
||||
s->Push(code->ptr()->static_calls_target_table_);
|
||||
}
|
||||
NOT_IN_PRODUCT(s->Push(code->ptr()->return_address_metadata_));
|
||||
#if !defined(PRODUCT)
|
||||
s->Push(code->ptr()->return_address_metadata_);
|
||||
if (FLAG_code_comments) {
|
||||
s->Push(code->ptr()->comments_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void WriteAlloc(Serializer* s) {
|
||||
|
@ -1448,8 +1456,12 @@ class CodeSerializationCluster : public SerializationCluster {
|
|||
WriteField(code, deopt_info_array_);
|
||||
WriteField(code, static_calls_target_table_);
|
||||
}
|
||||
NOT_IN_PRODUCT(WriteField(code, return_address_metadata_));
|
||||
|
||||
#if !defined(PRODUCT)
|
||||
WriteField(code, return_address_metadata_);
|
||||
if (FLAG_code_comments) {
|
||||
WriteField(code, comments_);
|
||||
}
|
||||
#endif
|
||||
s->Write<int32_t>(code->ptr()->state_bits_);
|
||||
}
|
||||
}
|
||||
|
@ -1565,7 +1577,9 @@ class CodeDeserializationCluster : public DeserializationCluster {
|
|||
#if !defined(PRODUCT)
|
||||
code->ptr()->return_address_metadata_ = d->ReadRef();
|
||||
code->ptr()->var_descriptors_ = LocalVarDescriptors::null();
|
||||
code->ptr()->comments_ = Array::null();
|
||||
code->ptr()->comments_ = FLAG_code_comments
|
||||
? reinterpret_cast<RawArray*>(d->ReadRef())
|
||||
: Array::null();
|
||||
code->ptr()->compile_timestamp_ = 0;
|
||||
#endif
|
||||
|
||||
|
@ -1573,13 +1587,29 @@ class CodeDeserializationCluster : public DeserializationCluster {
|
|||
}
|
||||
}
|
||||
|
||||
#if !(defined(DART_PRECOMPILED_RUNTIME) || defined(PRODUCT))
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
|
||||
if (!CodeObservers::AreActive()) return;
|
||||
if (!CodeObservers::AreActive() && !FLAG_support_disassembler) return;
|
||||
Code& code = Code::Handle(zone);
|
||||
Object& owner = Object::Handle(zone);
|
||||
for (intptr_t id = start_index_; id < stop_index_; id++) {
|
||||
code ^= refs.At(id);
|
||||
Code::NotifyCodeObservers(code, code.is_optimized());
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
if (CodeObservers::AreActive()) {
|
||||
Code::NotifyCodeObservers(code, code.is_optimized());
|
||||
}
|
||||
#endif
|
||||
owner = code.owner();
|
||||
if (owner.IsFunction()) {
|
||||
if ((FLAG_disassemble ||
|
||||
(code.is_optimized() && FLAG_disassemble_optimized)) &&
|
||||
FlowGraphPrinter::ShouldPrint(Function::Cast(owner))) {
|
||||
Disassembler::DisassembleCode(Function::Cast(owner), code,
|
||||
code.is_optimized());
|
||||
}
|
||||
} else if (FLAG_disassemble_stubs) {
|
||||
Disassembler::DisassembleStub(code.Name(), code);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // !DART_PRECOMPILED_RUNTIME
|
||||
|
|
|
@ -20,10 +20,6 @@ DEFINE_FLAG(bool,
|
|||
false,
|
||||
"Verify instructions offset in code object."
|
||||
"NOTE: This breaks the profiler.");
|
||||
DEFINE_FLAG(bool,
|
||||
code_comments,
|
||||
false,
|
||||
"Include comments into code and disassembly");
|
||||
#if defined(TARGET_ARCH_ARM)
|
||||
DEFINE_FLAG(bool, use_far_branches, false, "Enable far branches for ARM.");
|
||||
#endif
|
||||
|
|
|
@ -240,10 +240,14 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname,
|
|||
ASSERT(code.pointer_offsets_length() == 0);
|
||||
#endif
|
||||
|
||||
const ObjectPool& object_pool =
|
||||
ObjectPool::Handle(zone, code.GetObjectPool());
|
||||
if (!object_pool.IsNull()) {
|
||||
object_pool.DebugPrint();
|
||||
if (FLAG_use_bare_instructions) {
|
||||
THR_Print("(No object pool for bare instructions.)\n");
|
||||
} else {
|
||||
const ObjectPool& object_pool =
|
||||
ObjectPool::Handle(zone, code.GetObjectPool());
|
||||
if (!object_pool.IsNull()) {
|
||||
object_pool.DebugPrint();
|
||||
}
|
||||
}
|
||||
|
||||
THR_Print("PC Descriptors for function '%s' {\n", function_fullname);
|
||||
|
@ -258,23 +262,25 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname,
|
|||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
const Array& deopt_table = Array::Handle(zone, 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(zone);
|
||||
TypedData& info = TypedData::Handle(zone);
|
||||
Smi& reason_and_flags = Smi::Handle(zone);
|
||||
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, base + offset.Value(),
|
||||
DeoptInfo::ToCString(deopt_table, info),
|
||||
DeoptReasonToCString(static_cast<ICData::DeoptReasonId>(reason)));
|
||||
if (!deopt_table.IsNull()) {
|
||||
intptr_t deopt_table_length = DeoptTable::GetLength(deopt_table);
|
||||
if (deopt_table_length > 0) {
|
||||
THR_Print("DeoptInfo: {\n");
|
||||
Smi& offset = Smi::Handle(zone);
|
||||
TypedData& info = TypedData::Handle(zone);
|
||||
Smi& reason_and_flags = Smi::Handle(zone);
|
||||
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, base + offset.Value(),
|
||||
DeoptInfo::ToCString(deopt_table, info),
|
||||
DeoptReasonToCString(static_cast<ICData::DeoptReasonId>(reason)));
|
||||
}
|
||||
THR_Print("}\n");
|
||||
}
|
||||
THR_Print("}\n");
|
||||
}
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
|
@ -343,6 +349,9 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname,
|
|||
THR_Print("}\n");
|
||||
}
|
||||
|
||||
#if defined(DART_PRECOMPILED_RUNTIME)
|
||||
THR_Print("(Cannot show static call target functions in AOT runtime.)\n");
|
||||
#else
|
||||
{
|
||||
THR_Print("Static call target functions {\n");
|
||||
const auto& table = Array::Handle(zone, code.static_calls_target_table());
|
||||
|
@ -393,8 +402,9 @@ void Disassembler::DisassembleCodeHelper(const char* function_fullname,
|
|||
}
|
||||
}
|
||||
}
|
||||
THR_Print("}\n");
|
||||
}
|
||||
THR_Print("}\n");
|
||||
#endif // defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
if (optimized && FLAG_trace_inlining_intervals) {
|
||||
code.DumpInlineIntervals();
|
||||
|
@ -411,6 +421,20 @@ void Disassembler::DisassembleCode(const Function& function,
|
|||
DisassembleCodeHelper(function_fullname, code, optimized);
|
||||
}
|
||||
|
||||
void Disassembler::DisassembleStub(const char* name, const Code& code) {
|
||||
LogBlock lb;
|
||||
THR_Print("Code for stub '%s': {\n", name);
|
||||
DisassembleToStdout formatter;
|
||||
code.Disassemble(&formatter);
|
||||
THR_Print("}\n");
|
||||
const ObjectPool& object_pool = ObjectPool::Handle(code.object_pool());
|
||||
if (FLAG_use_bare_instructions) {
|
||||
THR_Print("(No object pool for bare instructions.)\n");
|
||||
} else if (!object_pool.IsNull()) {
|
||||
object_pool.DebugPrint();
|
||||
}
|
||||
}
|
||||
|
||||
#else // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
|
||||
void Disassembler::DisassembleCode(const Function& function,
|
||||
|
|
|
@ -184,6 +184,8 @@ class Disassembler : public AllStatic {
|
|||
const Code& code,
|
||||
bool optimized);
|
||||
|
||||
static void DisassembleStub(const char* name, const Code& code);
|
||||
|
||||
private:
|
||||
static void DisassembleCodeHelper(const char* function_fullname,
|
||||
const Code& code,
|
||||
|
|
|
@ -60,7 +60,6 @@ DEFINE_FLAG(int,
|
|||
"The scale of invocation count, by size of the function.");
|
||||
DEFINE_FLAG(bool, source_lines, false, "Emit source line as assembly comment.");
|
||||
|
||||
DECLARE_FLAG(bool, code_comments);
|
||||
DECLARE_FLAG(charp, deoptimize_filter);
|
||||
DECLARE_FLAG(bool, intrinsify);
|
||||
DECLARE_FLAG(int, regexp_optimization_counter_threshold);
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// 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.
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#include "vm/compiler/backend/il_printer.h"
|
||||
|
||||
#include "vm/compiler/backend/il.h"
|
||||
|
@ -15,18 +13,11 @@ namespace dart {
|
|||
|
||||
#if !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
|
||||
DEFINE_FLAG(bool,
|
||||
display_sorted_ic_data,
|
||||
false,
|
||||
"Calls display a unary, sorted-by count form of ICData");
|
||||
DEFINE_FLAG(bool, print_environments, false, "Print SSA environments.");
|
||||
DEFINE_FLAG(charp,
|
||||
print_flow_graph_filter,
|
||||
NULL,
|
||||
"Print only IR of functions with matching names");
|
||||
|
||||
DECLARE_FLAG(bool, trace_inlining_intervals);
|
||||
|
||||
// Checks whether function's name matches the given filter, which is
|
||||
// a comma-separated list of strings.
|
||||
bool FlowGraphPrinter::PassesFilter(const char* filter,
|
||||
|
@ -74,6 +65,16 @@ bool FlowGraphPrinter::ShouldPrint(const Function& function) {
|
|||
return PassesFilter(FLAG_print_flow_graph_filter, function);
|
||||
}
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
DEFINE_FLAG(bool,
|
||||
display_sorted_ic_data,
|
||||
false,
|
||||
"Calls display a unary, sorted-by count form of ICData");
|
||||
DEFINE_FLAG(bool, print_environments, false, "Print SSA environments.");
|
||||
|
||||
DECLARE_FLAG(bool, trace_inlining_intervals);
|
||||
|
||||
void FlowGraphPrinter::PrintGraph(const char* phase, FlowGraph* flow_graph) {
|
||||
LogBlock lb;
|
||||
THR_Print("*** BEGIN CFG\n%s\n", phase);
|
||||
|
@ -1181,8 +1182,12 @@ const char* Environment::ToCString() const {
|
|||
return Thread::Current()->zone()->MakeCopyOfString(buffer);
|
||||
}
|
||||
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#else // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
|
||||
#if !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
const char* Instruction::ToCString() const {
|
||||
return DebugName();
|
||||
}
|
||||
|
@ -1219,8 +1224,8 @@ bool FlowGraphPrinter::ShouldPrint(const Function& function) {
|
|||
return false;
|
||||
}
|
||||
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
||||
#endif // !defined(PRODUCT) || defined(FORCE_INCLUDE_DISASSEMBLER)
|
||||
|
||||
} // namespace dart
|
||||
|
||||
#endif // !defined(DART_PRECOMPILED_RUNTIME)
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
#include "vm/compiler/compiler_pass.h"
|
||||
#include "vm/compiler/jit/compiler.h"
|
||||
#include "vm/cpu.h"
|
||||
#include "vm/flag_list.h"
|
||||
|
||||
namespace dart {
|
||||
|
||||
DECLARE_FLAG(bool, code_comments);
|
||||
DECLARE_FLAG(bool, print_flow_graph);
|
||||
DECLARE_FLAG(bool, print_flow_graph_optimized);
|
||||
|
||||
|
@ -299,8 +299,8 @@ static bool IntrinsifyArraySetIndexed(FlowGraph* flow_graph,
|
|||
break;
|
||||
case kTypedDataInt32ArrayCid:
|
||||
case kExternalTypedDataInt32ArrayCid:
|
||||
// Use same truncating unbox-instruction for int32 and uint32.
|
||||
FALL_THROUGH;
|
||||
// Use same truncating unbox-instruction for int32 and uint32.
|
||||
FALL_THROUGH;
|
||||
case kTypedDataUint32ArrayCid:
|
||||
case kExternalTypedDataUint32ArrayCid:
|
||||
// Supports smi and mint, slow-case for bigints.
|
||||
|
|
|
@ -851,6 +851,10 @@ const char* Dart::FeaturesString(Isolate* isolate,
|
|||
buffer.AddString(FLAG_causal_async_stacks ? " causal_async_stacks"
|
||||
: " no-causal_async_stacks");
|
||||
|
||||
#if !defined(PRODUCT)
|
||||
buffer.AddString(FLAG_code_comments ? " code-comments"
|
||||
: " no-code-comments");
|
||||
#endif
|
||||
|
||||
// Generated code must match the host architecture and ABI.
|
||||
#if defined(TARGET_ARCH_ARM)
|
||||
|
|
|
@ -71,6 +71,8 @@ constexpr bool kDartUseBytecode = false;
|
|||
R(disassemble_optimized, false, bool, false, "Disassemble optimized code.") \
|
||||
R(disassemble_relative, false, bool, false, \
|
||||
"Use offsets instead of absolute PCs") \
|
||||
R(code_comments, false, bool, false, \
|
||||
"Include comments into code and disassembly.") \
|
||||
R(dump_megamorphic_stats, false, bool, false, \
|
||||
"Dump megamorphic cache statistics") \
|
||||
R(dump_symbol_stats, false, bool, false, "Dump symbol table statistics") \
|
||||
|
|
|
@ -74,15 +74,7 @@ RawCode* StubCode::Generate(
|
|||
/*optimized=*/false));
|
||||
#ifndef PRODUCT
|
||||
if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
|
||||
LogBlock lb;
|
||||
THR_Print("Code for stub '%s': {\n", name);
|
||||
DisassembleToStdout formatter;
|
||||
code.Disassemble(&formatter);
|
||||
THR_Print("}\n");
|
||||
const ObjectPool& object_pool = ObjectPool::Handle(code.object_pool());
|
||||
if (!object_pool.IsNull()) {
|
||||
object_pool.DebugPrint();
|
||||
}
|
||||
Disassembler::DisassembleStub(name, code);
|
||||
}
|
||||
#endif // !PRODUCT
|
||||
return code.raw();
|
||||
|
@ -199,15 +191,7 @@ RawCode* StubCode::GetAllocationStubForClass(const Class& cls) {
|
|||
Code::NotifyCodeObservers(name, stub, /*optimized=*/false);
|
||||
#ifndef PRODUCT
|
||||
if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
|
||||
LogBlock lb;
|
||||
THR_Print("Code for allocation stub '%s': {\n", name);
|
||||
DisassembleToStdout formatter;
|
||||
stub.Disassemble(&formatter);
|
||||
THR_Print("}\n");
|
||||
const ObjectPool& object_pool = ObjectPool::Handle(stub.object_pool());
|
||||
if (!object_pool.IsNull()) {
|
||||
object_pool.DebugPrint();
|
||||
}
|
||||
Disassembler::DisassembleStub(name, stub);
|
||||
}
|
||||
#endif // !PRODUCT
|
||||
}
|
||||
|
@ -245,15 +229,7 @@ RawCode* StubCode::GetBuildMethodExtractorStub(
|
|||
|
||||
#ifndef PRODUCT
|
||||
if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
|
||||
LogBlock lb;
|
||||
THR_Print("Code for isolate stub '%s': {\n", name);
|
||||
DisassembleToStdout formatter;
|
||||
stub.Disassemble(&formatter);
|
||||
THR_Print("}\n");
|
||||
const ObjectPool& object_pool = ObjectPool::Handle(stub.object_pool());
|
||||
if (!object_pool.IsNull()) {
|
||||
object_pool.DebugPrint();
|
||||
}
|
||||
Disassembler::DisassembleStub(name, stub);
|
||||
}
|
||||
#endif // !PRODUCT
|
||||
return stub.raw();
|
||||
|
|
|
@ -22,6 +22,8 @@ class RawCode;
|
|||
class SnapshotReader;
|
||||
class SnapshotWriter;
|
||||
|
||||
DECLARE_FLAG(bool, disassemble_stubs);
|
||||
|
||||
// Is it permitted for the stubs above to refer to Object::null(), which is
|
||||
// allocated in the VM isolate and shared across all isolates.
|
||||
// However, in cases where a simple GC-safe placeholder is needed on the stack,
|
||||
|
|
Loading…
Reference in a new issue