mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 16:44:59 +00:00
Updates to analyze_snapshot tool
- format fixes for readme and build descriptions - added pretty print option for tool - changed parsing of class_table to discover library objects - JSON output format changes - defines to accurately mirror platform compatibility - other small fixes TEST=ci Change-Id: I3f27f6fa48ce6111d94c5a88d57fa7bf7abc210c Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/252661 Reviewed-by: Slava Egorov <vegorov@google.com> Commit-Queue: Chris Evans <cmevans@google.com>
This commit is contained in:
parent
6e0a380e93
commit
7962f8a119
|
@ -10,6 +10,7 @@ bin/ Binaries/scripts to compile, run, and manage Dart apps.
|
|||
dart2js Dart to JavaScript production compiler
|
||||
dartdevc Dart to Javascript development compiler
|
||||
dartdoc Dart documentation generator
|
||||
analyze_snapshot Standalone tool to dump information on AOT snapshots
|
||||
|
||||
include/ header files that define the Dart embedding API for use by
|
||||
- C/C++ applications that embed the Dart Virtual machine
|
||||
|
|
|
@ -9,19 +9,24 @@
|
|||
#include "bin/options.h"
|
||||
#include "bin/platform.h"
|
||||
|
||||
#if !defined(DART_HOST_OS_FUCHSIA)
|
||||
#if defined(TARGET_ARCH_IS_64_BIT) && defined(DART_PRECOMPILED_RUNTIME) && \
|
||||
(defined(DART_TARGET_OS_ANDROID) || defined(DART_TARGET_OS_LINUX))
|
||||
#define SUPPORT_ANALYZE_SNAPSHOT
|
||||
#endif
|
||||
|
||||
#ifdef SUPPORT_ANALYZE_SNAPSHOT
|
||||
#include "include/analyze_snapshot_api.h"
|
||||
#endif
|
||||
|
||||
namespace dart {
|
||||
namespace bin {
|
||||
#if !defined(DART_HOST_OS_FUCHSIA)
|
||||
|
||||
#ifdef SUPPORT_ANALYZE_SNAPSHOT
|
||||
#define STRING_OPTIONS_LIST(V) V(out, out_path)
|
||||
|
||||
#define BOOL_OPTIONS_LIST(V) \
|
||||
V(help, help) \
|
||||
V(version, version)
|
||||
V(sdk_version, sdk_version) \
|
||||
V(pp, pp)
|
||||
|
||||
#define STRING_OPTION_DEFINITION(flag, variable) \
|
||||
static const char* variable = nullptr; \
|
||||
|
@ -43,11 +48,12 @@ static void PrintUsage() {
|
|||
"Common options: \n"
|
||||
"--help \n"
|
||||
" Display this message. \n"
|
||||
"--version \n"
|
||||
"--sdk_version \n"
|
||||
" Print the SDK version. \n"
|
||||
"--out \n"
|
||||
" Path to generate the analysis results JSON. \n"
|
||||
" \n"
|
||||
"--pp \n"
|
||||
" Flag to pretty-print analysis to stdout. \n"
|
||||
"If omitting [<vm-flags>] the VM parsing the snapshot is created with the \n"
|
||||
"following default flags: \n"
|
||||
"--enable_mirrors=false \n"
|
||||
|
@ -82,7 +88,7 @@ static int ParseArguments(int argc,
|
|||
i += 1;
|
||||
}
|
||||
|
||||
// Parse out the kernel inputs.
|
||||
// Parse out remaining inputs.
|
||||
while (i < argc) {
|
||||
inputs->AddArgument(argv[i]);
|
||||
i++;
|
||||
|
@ -91,7 +97,7 @@ static int ParseArguments(int argc,
|
|||
if (help) {
|
||||
PrintUsage();
|
||||
Platform::Exit(0);
|
||||
} else if (version) {
|
||||
} else if (sdk_version) {
|
||||
Syslog::PrintErr("Dart SDK version: %s\n", Dart_VersionString());
|
||||
Platform::Exit(0);
|
||||
}
|
||||
|
@ -101,13 +107,6 @@ static int ParseArguments(int argc,
|
|||
Syslog::PrintErr("At least one input is required\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (out_path == nullptr) {
|
||||
Syslog::PrintErr(
|
||||
"Please specify an output path for analysis with the --out flag.\n\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -237,10 +236,17 @@ int RunAnalyzer(int argc, char** argv) {
|
|||
intptr_t out_len = 0;
|
||||
|
||||
Dart_EnterScope();
|
||||
Dart_DumpSnapshotInformationAsJson(&out, &out_len, &info);
|
||||
WriteFile(out_path, out, out_len);
|
||||
// Since ownership of the JSON buffer is ours, free before we exit.
|
||||
free(out);
|
||||
if (out_path != nullptr) {
|
||||
Dart_DumpSnapshotInformationAsJson(&out, &out_len, &info);
|
||||
WriteFile(out_path, out, out_len);
|
||||
// Since ownership of the JSON buffer is ours, free before we exit.
|
||||
free(out);
|
||||
}
|
||||
|
||||
if (pp) {
|
||||
Dart_DumpSnapshotInformationPP(&info);
|
||||
}
|
||||
|
||||
Dart_ExitScope();
|
||||
Dart_ShutdownIsolate();
|
||||
// Unload our DartELF to avoid leaks
|
||||
|
@ -252,9 +258,14 @@ int RunAnalyzer(int argc, char** argv) {
|
|||
} // namespace dart
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#if !defined(DART_HOST_OS_FUCHSIA)
|
||||
#ifdef SUPPORT_ANALYZE_SNAPSHOT
|
||||
return dart::bin::RunAnalyzer(argc, argv);
|
||||
#endif
|
||||
dart::Syslog::PrintErr("Cannot run on Fuchsia.\n");
|
||||
#else
|
||||
dart::Syslog::PrintErr("Unsupported platform.\n");
|
||||
dart::Syslog::PrintErr(
|
||||
"Requires SDK with following "
|
||||
"flags:\n\tTARGET_ARCH_IS_64_BIT\n\tDART_PRECOMPILED_RUNTIME\n\tDART_"
|
||||
"TARGET_OS_ANDROID || DART_TARGET_OS_LINUX");
|
||||
return dart::bin::kErrorExitCode;
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@ typedef struct {
|
|||
void Dart_DumpSnapshotInformationAsJson(char** buffer,
|
||||
intptr_t* buffer_length,
|
||||
Dart_SnapshotAnalyzerInformation* info);
|
||||
|
||||
void Dart_DumpSnapshotInformationPP(Dart_SnapshotAnalyzerInformation* info);
|
||||
|
||||
} // namespace snapshot_analyzer
|
||||
} // namespace dart
|
||||
|
||||
|
|
|
@ -349,11 +349,6 @@ cc/CodeExecutability: SkipByDesign # --dual-map-code not supported on non-Linux/
|
|||
dart/v8_snapshot_profile_writer_test: SkipByDesign # Only relevant for AOT. Doesn't work in cross-compilation (has to run on the host). On Linux/simarm64 and Linux/simarm this test requires buildtools/clang which is not always available on testing shards.
|
||||
dart_2/v8_snapshot_profile_writer_test: SkipByDesign # Only relevant for AOT. Doesn't work in cross-compilation (has to run on the host). On Linux/simarm64 and Linux/simarm this test requires buildtools/clang which is not always available on testing shards.
|
||||
|
||||
# Currently this is only supported on 64-bit linux systems with precompilation
|
||||
[ $arch == ia32 || $arch == simarm || $arch == simarm64 || $arch == simarm64c || $arch == simriscv32 || $arch == simriscv64 || $compiler != dartk || $system == fuchsia || $system != linux ]
|
||||
dart/analyze_snapshot_binary_test: SkipByDesign # Only run on 64bit AOT on standard architectures
|
||||
dart_2/analyze_snapshot_binary_test: SkipByDesign # Only run on 64bit AOT on standard architectures
|
||||
|
||||
# On the simluator stack traces produced by the Profiler do not match
|
||||
# up with the real Dart stack trace and hence we don't get correct
|
||||
# symbol names.
|
||||
|
@ -452,6 +447,11 @@ dart_2/causal_stacks/zone_callback_stack_traces_test: SkipByDesign # Asserts exa
|
|||
dart/data_uri*test: Skip # Data uri's not supported by dart2js or the analyzer.
|
||||
dart_2/data_uri*test: Skip # Data uri's not supported by dart2js or the analyzer.
|
||||
|
||||
# Currently this is only supported on 64-bit linux systems with precompilation
|
||||
[ $compiler != dartk || $mode == debug || $runtime != dart_precompiled || $arch != arm64 && $arch != x64 || $system != android && $system != linux ]
|
||||
dart/analyze_snapshot_binary_test: SkipByDesign # Only run on 64bit AOT on standard architectures
|
||||
dart_2/analyze_snapshot_binary_test: SkipByDesign # Only run on 64bit AOT on standard architectures
|
||||
|
||||
[ $mode == debug || $runtime != dart_precompiled || $system == android ]
|
||||
dart/emit_aot_size_info_flag_test: SkipByDesign # This test is for VM AOT only and is quite slow (so we don't run it in debug mode).
|
||||
dart/split_aot_kernel_generation2_test: SkipByDesign # This test is for VM AOT only and is quite slow (so we don't run it in debug mode).
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
// Copyright (c) 2021, 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 <set>
|
||||
#include <sstream>
|
||||
#include "include/analyze_snapshot_api.h"
|
||||
#include "vm/dart_api_impl.h"
|
||||
#include "vm/json_writer.h"
|
||||
|
@ -11,111 +12,188 @@
|
|||
|
||||
namespace dart {
|
||||
namespace snapshot_analyzer {
|
||||
void DumpClassTable(Thread* thread, dart::JSONWriter* js) {
|
||||
|
||||
void DumpFunctionJSON(Dart_SnapshotAnalyzerInformation* info,
|
||||
dart::JSONWriter* js,
|
||||
const Function& function) {
|
||||
String& signature = String::Handle(function.InternalSignature());
|
||||
Code& code = Code::Handle(function.CurrentCode());
|
||||
// On different architectures the type of the underlying
|
||||
// dart::uword can result in an unsigned long long vs unsigned long
|
||||
// mismatch.
|
||||
const auto code_addr = static_cast<uint64_t>(code.PayloadStart());
|
||||
|
||||
const auto isolate_instructions_base =
|
||||
reinterpret_cast<uint64_t>(info->vm_isolate_instructions);
|
||||
const auto vm_instructions_base =
|
||||
reinterpret_cast<uint64_t>(info->vm_snapshot_instructions);
|
||||
uint64_t relative_offset;
|
||||
const char* section;
|
||||
|
||||
js->OpenObject();
|
||||
js->PrintProperty("name", function.ToCString());
|
||||
js->PrintProperty("signature", signature.ToCString());
|
||||
// Should not have code that is located in
|
||||
// _kDartVmSnapshotInstructions, but check in case.
|
||||
if (code_addr < isolate_instructions_base) {
|
||||
relative_offset = code_addr - vm_instructions_base;
|
||||
section = "_kDartVmSnapshotInstructions";
|
||||
} else {
|
||||
relative_offset = code_addr - isolate_instructions_base;
|
||||
section = "_kDartIsolateSnapshotInstructions";
|
||||
}
|
||||
js->PrintProperty("offset", static_cast<intptr_t>(relative_offset));
|
||||
js->PrintProperty("section", section);
|
||||
js->PrintProperty("size", static_cast<intptr_t>(code.Size()));
|
||||
js->CloseObject();
|
||||
}
|
||||
void DumpClassTableJSON(Thread* thread,
|
||||
Dart_SnapshotAnalyzerInformation* info,
|
||||
dart::JSONWriter* js) {
|
||||
auto class_table = thread->isolate_group()->class_table();
|
||||
|
||||
Class& cls = Class::Handle();
|
||||
String& name = String::Handle();
|
||||
js->OpenArray("class_table");
|
||||
js->OpenObject("class_table");
|
||||
|
||||
// Note: Parse all top-level library functions first
|
||||
// separately, then on second pass just include name of
|
||||
// the library the class belongs to
|
||||
// TODO(#47924): Can clean this up to require single pass
|
||||
js->OpenArray("libs");
|
||||
std::set<uintptr_t> lib_hashes;
|
||||
|
||||
// Note: We start counting at index = 1 mirroring other
|
||||
// locations that iterate through class_table().
|
||||
// HasValidClassAt(0) will crash on DEBUG ASSERT.
|
||||
for (intptr_t i = 1; i < class_table->NumCids(); i++) {
|
||||
if (!class_table->HasValidClassAt(i)) {
|
||||
continue;
|
||||
}
|
||||
cls = class_table->At(i);
|
||||
if (!cls.IsNull()) {
|
||||
name = cls.Name();
|
||||
js->OpenObject();
|
||||
js->PrintProperty("id", i);
|
||||
js->PrintProperty("name", name.ToCString());
|
||||
if (cls.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
const Library& lib = Library::Handle(cls.library());
|
||||
if (lib.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
String& lib_name = String::Handle(lib.url());
|
||||
uintptr_t lib_hash = lib_name.Hash();
|
||||
if (lib_hashes.count(lib_hash) != 0u) {
|
||||
continue;
|
||||
}
|
||||
lib_hashes.insert(lib_hash);
|
||||
Class& toplevel_cls = Class::Handle(lib.toplevel_class());
|
||||
Array& toplevel_funcs = Array::Handle(toplevel_cls.functions());
|
||||
|
||||
// Note: Some meta info is stripped from the snapshot, it's important
|
||||
// to check every field for NULL to avoid segfaults.
|
||||
const Library& library = Library::Handle(cls.library());
|
||||
if (!library.IsNull()) {
|
||||
String& lib_name = String::Handle();
|
||||
lib_name = String::NewFormatted(
|
||||
Heap::kOld, "%s%s", String::Handle(library.url()).ToCString(),
|
||||
String::Handle(library.private_key()).ToCString());
|
||||
js->PrintProperty("library", lib_name.ToCString());
|
||||
}
|
||||
|
||||
const AbstractType& super_type = AbstractType::Handle(cls.super_type());
|
||||
if (super_type.IsNull()) {
|
||||
} else {
|
||||
const String& super_name = String::Handle(super_type.Name());
|
||||
js->PrintProperty("super_class", super_name.ToCString());
|
||||
}
|
||||
|
||||
const Array& interfaces_array = Array::Handle(cls.interfaces());
|
||||
if (!interfaces_array.IsNull()) {
|
||||
if (interfaces_array.Length() > 0) {
|
||||
js->OpenArray("interfaces");
|
||||
AbstractType& interface = AbstractType::Handle();
|
||||
intptr_t len = interfaces_array.Length();
|
||||
for (intptr_t i = 0; i < len; i++) {
|
||||
interface ^= interfaces_array.At(i);
|
||||
js->PrintValue(interface.ToCString());
|
||||
}
|
||||
js->CloseArray();
|
||||
}
|
||||
}
|
||||
const Array& functions_array = Array::Handle(cls.functions());
|
||||
if (!functions_array.IsNull()) {
|
||||
if (functions_array.Length() > 0) {
|
||||
js->OpenArray("functions");
|
||||
Function& function = Function::Handle();
|
||||
intptr_t len = functions_array.Length();
|
||||
for (intptr_t i = 0; i < len; i++) {
|
||||
function ^= functions_array.At(i);
|
||||
if (function.IsNull() || !function.HasCode()) {
|
||||
continue;
|
||||
}
|
||||
const Code& code = Code::Handle(function.CurrentCode());
|
||||
intptr_t size = code.Size();
|
||||
|
||||
// Note: Some entry points here will be pointing to the VM
|
||||
// instructions buffer.
|
||||
|
||||
// Note: code_entry will contain the address in the memory
|
||||
// In order to resolve it to a relative offset in the instructions
|
||||
// buffer we need to pick the base address and substract it from
|
||||
// the entry point address.
|
||||
auto code_entry = code.EntryPoint();
|
||||
// On different architectures the type of the underlying
|
||||
// dart::uword can result in an unsigned long long vs unsigned long
|
||||
// mismatch.
|
||||
uint64_t code_addr = static_cast<uint64_t>(code_entry);
|
||||
js->OpenObject();
|
||||
js->PrintProperty("name", function.ToCString());
|
||||
js->PrintfProperty("code_entry", "0x%" PRIx64 "", code_addr);
|
||||
js->PrintProperty("size", size);
|
||||
js->CloseObject();
|
||||
}
|
||||
js->CloseArray();
|
||||
}
|
||||
}
|
||||
const Array& fields_array = Array::Handle(cls.fields());
|
||||
if (fields_array.IsNull()) {
|
||||
} else {
|
||||
if (fields_array.Length() > 0) {
|
||||
js->OpenArray("fields");
|
||||
Field& field = Field::Handle();
|
||||
for (intptr_t i = 0; i < fields_array.Length(); i++) {
|
||||
field ^= fields_array.At(i);
|
||||
js->PrintValue(field.ToCString());
|
||||
}
|
||||
js->CloseArray();
|
||||
}
|
||||
js->OpenObject();
|
||||
js->PrintProperty("name", lib_name.ToCString());
|
||||
if (toplevel_funcs.Length() > 0) {
|
||||
js->OpenArray("functions");
|
||||
for (intptr_t j = 0; j < toplevel_funcs.Length(); j++) {
|
||||
Function& function =
|
||||
Function::Handle(toplevel_cls.FunctionFromIndex(j));
|
||||
DumpFunctionJSON(info, js, function);
|
||||
}
|
||||
js->CloseArray();
|
||||
}
|
||||
js->CloseObject();
|
||||
}
|
||||
js->CloseArray();
|
||||
}
|
||||
void DumpObjectPool(Thread* thread, dart::JSONWriter* js) {
|
||||
js->OpenArray("object_pool");
|
||||
|
||||
js->OpenArray("classes");
|
||||
for (intptr_t i = 1; i < class_table->NumCids(); i++) {
|
||||
if (!class_table->HasValidClassAt(i)) {
|
||||
continue;
|
||||
}
|
||||
cls = class_table->At(i);
|
||||
if (cls.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
js->OpenObject();
|
||||
String& name = String::Handle();
|
||||
name = cls.Name();
|
||||
js->PrintProperty("id", i);
|
||||
js->PrintProperty("name", name.ToCString());
|
||||
|
||||
// Note: Some meta info is stripped from the snapshot, it's important
|
||||
// to check for NULL periodically to avoid segfaults.
|
||||
const AbstractType& super_type = AbstractType::Handle(cls.super_type());
|
||||
if (!super_type.IsNull()) {
|
||||
const String& super_name = String::Handle(super_type.Name());
|
||||
js->PrintProperty("super_class", super_name.ToCString());
|
||||
}
|
||||
|
||||
const Array& interfaces_array = Array::Handle(cls.interfaces());
|
||||
if (!interfaces_array.IsNull() && interfaces_array.Length() > 0) {
|
||||
js->OpenArray("interfaces");
|
||||
AbstractType& interface = AbstractType::Handle();
|
||||
intptr_t len = interfaces_array.Length();
|
||||
for (intptr_t i = 0; i < len; i++) {
|
||||
interface ^= interfaces_array.At(i);
|
||||
js->PrintValue(interface.ToCString());
|
||||
}
|
||||
js->CloseArray();
|
||||
}
|
||||
|
||||
const Array& fields_array = Array::Handle(cls.fields());
|
||||
if (!fields_array.IsNull() && fields_array.Length() > 0) {
|
||||
js->OpenArray("fields");
|
||||
Field& field = Field::Handle();
|
||||
AbstractType& field_type = AbstractType::Handle();
|
||||
for (intptr_t i = 0; i < fields_array.Length(); i++) {
|
||||
field ^= fields_array.At(i);
|
||||
if (!field.IsNull()) {
|
||||
field_type = field.type();
|
||||
js->OpenObject();
|
||||
js->PrintProperty("name", String::Handle(field.name()).ToCString());
|
||||
js->PrintProperty("type",
|
||||
String::Handle(field_type.Name()).ToCString());
|
||||
if (field.is_static()) {
|
||||
Object& field_instance = Object::Handle();
|
||||
field_instance = field.StaticValue();
|
||||
js->PrintProperty("value", field_instance.ToCString());
|
||||
} else {
|
||||
js->PrintProperty("value", "non-static");
|
||||
}
|
||||
js->CloseObject();
|
||||
}
|
||||
}
|
||||
js->CloseArray();
|
||||
}
|
||||
|
||||
const Array& functions_array = Array::Handle(cls.functions());
|
||||
if (!functions_array.IsNull() && functions_array.Length() > 0) {
|
||||
js->OpenArray("functions");
|
||||
Function& function = Function::Handle();
|
||||
|
||||
intptr_t len = functions_array.Length();
|
||||
for (intptr_t i = 0; i < len; i++) {
|
||||
function ^= functions_array.At(i);
|
||||
if (function.IsNull() || !function.HasCode()) {
|
||||
continue;
|
||||
}
|
||||
if (function.IsLocalFunction()) {
|
||||
function = function.parent_function();
|
||||
}
|
||||
DumpFunctionJSON(info, js, function);
|
||||
}
|
||||
js->CloseArray();
|
||||
}
|
||||
|
||||
Library& library = Library::Handle(cls.library());
|
||||
if (!library.IsNull()) {
|
||||
js->PrintProperty("lib", String::Handle(library.url()).ToCString());
|
||||
}
|
||||
js->CloseObject();
|
||||
}
|
||||
js->CloseArray();
|
||||
|
||||
js->CloseObject();
|
||||
}
|
||||
void DumpObjectPoolJSON(Thread* thread, dart::JSONWriter* js) {
|
||||
js->OpenArray("object_pool");
|
||||
auto pool_ptr = thread->isolate_group()->object_store()->global_object_pool();
|
||||
const auto& pool = ObjectPool::Handle(ObjectPool::RawCast(pool_ptr));
|
||||
for (intptr_t i = 0; i < pool.Length(); i++) {
|
||||
|
@ -132,7 +210,6 @@ void DumpObjectPool(Thread* thread, dart::JSONWriter* js) {
|
|||
}
|
||||
|
||||
intptr_t cid = entry.GetClassId();
|
||||
|
||||
switch (cid) {
|
||||
case kOneByteStringCid: {
|
||||
js->OpenObject();
|
||||
|
@ -147,6 +224,7 @@ void DumpObjectPool(Thread* thread, dart::JSONWriter* js) {
|
|||
// TODO(#47924): Add support.
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// TODO(#47924): Investigate other types of objects to parse.
|
||||
break;
|
||||
|
@ -166,6 +244,157 @@ void DumpObjectPool(Thread* thread, dart::JSONWriter* js) {
|
|||
// }
|
||||
// }
|
||||
|
||||
void DumpFunctionPP(Dart_SnapshotAnalyzerInformation* info,
|
||||
std::stringstream& ss,
|
||||
const Function& function) {
|
||||
String& signature = String::Handle(function.InternalSignature());
|
||||
Code& code = Code::Handle(function.CurrentCode());
|
||||
// On different architectures the type of the underlying
|
||||
// dart::uword can result in an unsigned long long vs unsigned long
|
||||
// mismatch.
|
||||
const auto code_addr = static_cast<uint64_t>(code.PayloadStart());
|
||||
|
||||
const auto isolate_instructions_base =
|
||||
reinterpret_cast<uint64_t>(info->vm_isolate_instructions);
|
||||
const auto vm_instructions_base =
|
||||
reinterpret_cast<uint64_t>(info->vm_snapshot_instructions);
|
||||
uint64_t relative_offset;
|
||||
const char* section;
|
||||
|
||||
ss << "\t" << function.ToCString() << " " << signature.ToCString()
|
||||
<< " {\n\n";
|
||||
char offset_buff[100] = "";
|
||||
// Should not have code that is located in
|
||||
// _kDartVmSnapshotInstructions, but check in case.
|
||||
if (code_addr < isolate_instructions_base) {
|
||||
relative_offset = code_addr - vm_instructions_base;
|
||||
section = "_kDartVmSnapshotInstructions";
|
||||
} else {
|
||||
relative_offset = code_addr - isolate_instructions_base;
|
||||
section = "_kDartIsolateSnapshotInstructions";
|
||||
}
|
||||
|
||||
// Can not calculate for function without payload start
|
||||
if (code_addr == 0) {
|
||||
snprintf(offset_buff, sizeof(offset_buff), "Offset: <Could not read>");
|
||||
} else {
|
||||
snprintf(offset_buff, sizeof(offset_buff), "Offset: %s + 0x%" PRIx64 "",
|
||||
section, relative_offset);
|
||||
}
|
||||
ss << "\t\t" << offset_buff << "\n\n\t}\n";
|
||||
}
|
||||
// TODO(#47924): Refactor and reduce code duplication.
|
||||
void DumpClassTablePP(Thread* thread, Dart_SnapshotAnalyzerInformation* info) {
|
||||
std::stringstream ss;
|
||||
auto class_table = thread->isolate_group()->class_table();
|
||||
Class& cls = Class::Handle();
|
||||
std::set<uintptr_t> lib_hashes;
|
||||
for (intptr_t i = 1; i < class_table->NumCids(); i++) {
|
||||
if (!class_table->HasValidClassAt(i)) {
|
||||
continue;
|
||||
}
|
||||
cls = class_table->At(i);
|
||||
if (cls.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
const Library& lib = Library::Handle(cls.library());
|
||||
if (lib.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
String& lib_name = String::Handle(lib.url());
|
||||
uintptr_t lib_hash = lib_name.Hash();
|
||||
if (lib_hashes.count(lib_hash) != 0u) {
|
||||
continue;
|
||||
}
|
||||
lib_hashes.insert(lib_hash);
|
||||
Class& toplevel_cls = Class::Handle(lib.toplevel_class());
|
||||
Array& toplevel_funcs = Array::Handle(toplevel_cls.functions());
|
||||
if (toplevel_funcs.Length() > 0) {
|
||||
ss << "\nLibrary: " << lib_name.ToCString() << " {\n\n";
|
||||
for (intptr_t i = 0; i < toplevel_funcs.Length(); i++) {
|
||||
Function& function =
|
||||
Function::Handle(toplevel_cls.FunctionFromIndex(i));
|
||||
DumpFunctionPP(info, ss, function);
|
||||
}
|
||||
ss << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
for (intptr_t i = 1; i < class_table->NumCids(); i++) {
|
||||
if (!class_table->HasValidClassAt(i)) {
|
||||
continue;
|
||||
}
|
||||
cls = class_table->At(i);
|
||||
if (cls.IsNull()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ss << "\n";
|
||||
ss << cls.ToCString();
|
||||
const AbstractType& super_type = AbstractType::Handle(cls.super_type());
|
||||
if (!super_type.IsNull()) {
|
||||
const String& super_name = String::Handle(super_type.Name());
|
||||
ss << " extends " << super_name.ToCString();
|
||||
}
|
||||
|
||||
const Array& interfaces_array = Array::Handle(cls.interfaces());
|
||||
if (!interfaces_array.IsNull() && interfaces_array.Length() > 0) {
|
||||
AbstractType& interface = AbstractType::Handle();
|
||||
intptr_t len = interfaces_array.Length();
|
||||
bool implements_flag = true;
|
||||
for (intptr_t i = 0; i < len; i++) {
|
||||
interface ^= interfaces_array.At(i);
|
||||
if (implements_flag) {
|
||||
ss << " implements ";
|
||||
implements_flag = false;
|
||||
} else {
|
||||
ss << ", ";
|
||||
}
|
||||
ss << interface.ToCString();
|
||||
}
|
||||
}
|
||||
ss << " {\n\n";
|
||||
const Array& fields_array = Array::Handle(cls.fields());
|
||||
if (!fields_array.IsNull() && fields_array.Length() > 0) {
|
||||
Field& field = Field::Handle();
|
||||
AbstractType& field_type = AbstractType::Handle();
|
||||
for (intptr_t i = 0; i < fields_array.Length(); i++) {
|
||||
field ^= fields_array.At(i);
|
||||
if (!field.IsNull()) {
|
||||
field_type = field.type();
|
||||
ss << " " << String::Handle(field_type.Name()).ToCString();
|
||||
ss << " " << String::Handle(field.name()).ToCString();
|
||||
ss << " = ";
|
||||
if (field.is_static()) {
|
||||
Object& field_instance = Object::Handle();
|
||||
field_instance = field.StaticValue();
|
||||
ss << field_instance.ToCString() << "\n";
|
||||
} else {
|
||||
ss << "non-static;\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const Array& functions_array = Array::Handle(cls.functions());
|
||||
if (!functions_array.IsNull() && functions_array.Length() > 0) {
|
||||
Function& function = Function::Handle();
|
||||
intptr_t len = functions_array.Length();
|
||||
for (intptr_t i = 0; i < len; i++) {
|
||||
function ^= functions_array.At(i);
|
||||
if (function.IsNull() || !function.HasCode()) {
|
||||
continue;
|
||||
}
|
||||
if (function.IsLocalFunction()) {
|
||||
function = function.parent_function();
|
||||
}
|
||||
DumpFunctionPP(info, ss, function);
|
||||
}
|
||||
}
|
||||
ss << "\n}\n";
|
||||
}
|
||||
OS::Print("%s", ss.str().c_str());
|
||||
}
|
||||
|
||||
void Dart_DumpSnapshotInformationAsJson(
|
||||
char** buffer,
|
||||
intptr_t* buffer_length,
|
||||
|
@ -188,8 +417,8 @@ void Dart_DumpSnapshotInformationAsJson(
|
|||
// Debug builds assert that our thread has a lock before accessing
|
||||
// vm internal fields.
|
||||
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
|
||||
DumpClassTable(thread, &js);
|
||||
DumpObjectPool(thread, &js);
|
||||
DumpClassTableJSON(thread, info, &js);
|
||||
DumpObjectPoolJSON(thread, &js);
|
||||
}
|
||||
|
||||
// Close our empty object.
|
||||
|
@ -198,5 +427,19 @@ void Dart_DumpSnapshotInformationAsJson(
|
|||
// Give ownership to caller.
|
||||
js.Steal(buffer, buffer_length);
|
||||
}
|
||||
|
||||
void Dart_DumpSnapshotInformationPP(Dart_SnapshotAnalyzerInformation* info) {
|
||||
Thread* thread = Thread::Current();
|
||||
DARTSCOPE(thread);
|
||||
OS::Print("File information:\n\n");
|
||||
OS::Print("vm_data: %p\n", info->vm_snapshot_data);
|
||||
OS::Print("vm_instructions: %p\n", info->vm_snapshot_instructions);
|
||||
OS::Print("isolate_data: %p\n", info->vm_isolate_data);
|
||||
OS::Print("isolate_instructions: %p\n", info->vm_isolate_instructions);
|
||||
{
|
||||
SafepointReadRwLocker ml(thread, thread->isolate_group()->program_lock());
|
||||
DumpClassTablePP(thread, info);
|
||||
}
|
||||
}
|
||||
} // namespace snapshot_analyzer
|
||||
} // namespace dart
|
||||
|
|
Loading…
Reference in a new issue