From 7962f8a1192b50cbe7aaba838f20893496c1ca87 Mon Sep 17 00:00:00 2001 From: Chris Evans Date: Tue, 23 Aug 2022 12:18:00 +0000 Subject: [PATCH] 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 Commit-Queue: Chris Evans --- README.dart-sdk | 1 + runtime/bin/analyze_snapshot.cc | 55 +-- runtime/include/analyze_snapshot_api.h | 3 + runtime/tests/vm/vm.status | 10 +- runtime/vm/analyze_snapshot_api_impl.cc | 429 +++++++++++++++++++----- 5 files changed, 378 insertions(+), 120 deletions(-) diff --git a/README.dart-sdk b/README.dart-sdk index 61ffe7ef394..f76fd11e3bd 100644 --- a/README.dart-sdk +++ b/README.dart-sdk @@ -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 diff --git a/runtime/bin/analyze_snapshot.cc b/runtime/bin/analyze_snapshot.cc index 5a9011eb6e8..1e6f9a3bae2 100644 --- a/runtime/bin/analyze_snapshot.cc +++ b/runtime/bin/analyze_snapshot.cc @@ -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 [] 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 } diff --git a/runtime/include/analyze_snapshot_api.h b/runtime/include/analyze_snapshot_api.h index e02f461cc27..0e68d5ccb8f 100644 --- a/runtime/include/analyze_snapshot_api.h +++ b/runtime/include/analyze_snapshot_api.h @@ -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 diff --git a/runtime/tests/vm/vm.status b/runtime/tests/vm/vm.status index 607d08c068b..7eb612a110e 100644 --- a/runtime/tests/vm/vm.status +++ b/runtime/tests/vm/vm.status @@ -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). diff --git a/runtime/vm/analyze_snapshot_api_impl.cc b/runtime/vm/analyze_snapshot_api_impl.cc index ef73e763405..fa45dd63e42 100644 --- a/runtime/vm/analyze_snapshot_api_impl.cc +++ b/runtime/vm/analyze_snapshot_api_impl.cc @@ -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 +#include #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(code.PayloadStart()); + + const auto isolate_instructions_base = + reinterpret_cast(info->vm_isolate_instructions); + const auto vm_instructions_base = + reinterpret_cast(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(relative_offset)); + js->PrintProperty("section", section); + js->PrintProperty("size", static_cast(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 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(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(code.PayloadStart()); + + const auto isolate_instructions_base = + reinterpret_cast(info->vm_isolate_instructions); + const auto vm_instructions_base = + reinterpret_cast(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: "); + } 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 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