Re-land "[vm/aot] Snapshot size analysis."

Minor fixes have been applied, see diff against patchset 1.

This reverts commit 9b937f1226.

Change-Id: I8e4bbc0b88e33d3b554e91c17d1f849e24a1ccb3
Cq-Include-Trybots: luci.dart.try:vm-kernel-optcounter-threshold-linux-release-x64-try, vm-kernel-precomp-linux-debug-x64-try, vm-kernel-precomp-linux-release-simarm-try, vm-kernel-precomp-linux-release-simarm64-try, vm-kernel-precomp-linux-release-x64-try, vm-kernel-win-product-x64-try, vm-kernel-precomp-win-release-simarm64-try, vm-kernel-precomp-linux-release-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/84845
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Auto-Submit: Samir Jindel <sjindel@google.com>
This commit is contained in:
Samir Jindel 2018-11-22 15:14:46 +00:00 committed by commit-bot@chromium.org
parent 5ced1f1cd2
commit cde4270e47
14 changed files with 1204 additions and 76 deletions

View file

@ -0,0 +1,239 @@
// Copyright (c) 2018, 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.
import "package:expect/expect.dart";
import "package:dart2js_info/src/graph.dart";
class _NodeInfo {
int type;
int name;
int id;
int selfSize;
int edgeCount;
_NodeInfo(
this.type,
this.name,
this.id,
this.selfSize,
this.edgeCount,
);
}
const List<String> _kRequiredNodeFields = [
"type",
"name",
"id",
"self_size",
"edge_count",
];
class _EdgeInfo {
int type;
int nameOrIndex;
int nodeOffset;
_EdgeInfo(
this.type,
this.nameOrIndex,
this.nodeOffset,
);
}
const List<String> _kRequiredEdgeFields = [
"type",
"name_or_index",
"to_node",
];
class NodeInfo {
String type;
String name;
int id;
int selfSize;
NodeInfo(
this.type,
this.name,
this.id,
this.selfSize,
);
}
class V8SnapshotProfile extends Graph<int> {
// Indexed by node offset.
final Map<int, _NodeInfo> _nodes = {};
// Indexed by start node offset.
final Map<int, List<_EdgeInfo>> _toEdges = {};
final Map<int, List<_EdgeInfo>> _fromEdges = {};
List<String> _nodeFields = [];
List<String> _edgeFields = [];
List<String> _nodeTypes = [];
List<String> _edgeTypes = [];
List<String> _strings = [];
// Only used to ensure IDs are unique.
Set<int> _ids = Set<int>();
V8SnapshotProfile.fromJson(Map top) {
final Map snapshot = top["snapshot"];
_parseMetadata(snapshot["meta"]);
_parseStrings(top["strings"]);
Expect.equals(snapshot["node_count"], _parseNodes(top["nodes"]));
Expect.equals(snapshot["edge_count"], _parseEdges(top["edges"]));
_calculateFromEdges();
}
void _parseMetadata(Map meta) {
final List nodeFields = meta["node_fields"];
nodeFields.forEach(_nodeFields.add);
Expect.isTrue(_kRequiredNodeFields.every(_nodeFields.contains));
final List edgeFields = meta["edge_fields"];
edgeFields.forEach(_edgeFields.add);
Expect.isTrue(_kRequiredEdgeFields.every(_edgeFields.contains));
// First entry of "node_types" is an array with the actual node types. IDK
// what the other entries are for.
List nodeTypes = meta["node_types"];
nodeTypes = nodeTypes[0];
nodeTypes.forEach(_nodeTypes.add);
// Same for edges.
List edgeTypes = meta["edge_types"];
edgeTypes = edgeTypes[0];
edgeTypes.forEach(_edgeTypes.add);
}
int _parseNodes(List nodes) {
final int typeIndex = _nodeFields.indexOf("type");
final int nameIndex = _nodeFields.indexOf("name");
final int idIndex = _nodeFields.indexOf("id");
final int selfSizeIndex = _nodeFields.indexOf("self_size");
final int edgeCountIndex = _nodeFields.indexOf("edge_count");
int offset = 0;
for (; offset < nodes.length; offset += _nodeFields.length) {
final int type = nodes[offset + typeIndex];
Expect.isTrue(0 <= type && type < _nodeTypes.length);
final int name = nodes[offset + nameIndex];
Expect.isTrue(0 <= name && name < _strings.length);
final int id = nodes[offset + idIndex];
Expect.isTrue(id >= 0);
Expect.isFalse(_ids.contains(id));
_ids.add(id);
final int selfSize = nodes[offset + selfSizeIndex];
Expect.isTrue(selfSize >= 0);
final int edgeCount = nodes[offset + edgeCountIndex];
Expect.isTrue(edgeCount >= 0);
_nodes[offset] = _NodeInfo(type, name, id, selfSize, edgeCount);
}
Expect.equals(offset, nodes.length);
return offset ~/ _nodeFields.length;
}
int _parseEdges(List edges) {
final int typeIndex = _edgeFields.indexOf("type");
final int nameOrIndexIndex = _edgeFields.indexOf("name_or_index");
final int toNodeIndex = _edgeFields.indexOf("to_node");
int edgeOffset = 0;
for (int nodeOffset = 0;
nodeOffset < _nodes.length * _nodeFields.length;
nodeOffset += _nodeFields.length) {
final int edgeCount = _nodes[nodeOffset].edgeCount;
final List<_EdgeInfo> nodeEdges = List<_EdgeInfo>(edgeCount);
for (int i = 0; i < edgeCount; ++i, edgeOffset += _edgeFields.length) {
final int type = edges[edgeOffset + typeIndex];
Expect.isTrue(0 <= type && type < _edgeTypes.length);
final int nameOrIndex = edges[edgeOffset + nameOrIndexIndex];
if (_edgeTypes[type] == "property") {
Expect.isTrue(0 <= nameOrIndex && nameOrIndex < _strings.length);
} else if (_edgeTypes[type] == "element") {
Expect.isTrue(nameOrIndex >= 0);
}
final int toNode = edges[edgeOffset + toNodeIndex];
checkNode(toNode);
nodeEdges[i] = _EdgeInfo(type, nameOrIndex, toNode);
}
_toEdges[nodeOffset] = nodeEdges;
}
Expect.equals(edgeOffset, edges.length);
return edgeOffset ~/ _edgeFields.length;
}
void checkNode(int offset) {
Expect.isTrue(offset >= 0 &&
offset % _nodeFields.length == 0 &&
offset ~/ _nodeFields.length < _nodes.length);
}
void _calculateFromEdges() {
for (final MapEntry<int, List<_EdgeInfo>> entry in _toEdges.entries) {
final int fromNode = entry.key;
for (final _EdgeInfo edge in entry.value) {
final List<_EdgeInfo> backEdges =
_fromEdges.putIfAbsent(edge.nodeOffset, () => <_EdgeInfo>[]);
backEdges.add(_EdgeInfo(edge.type, edge.nameOrIndex, fromNode));
}
}
}
void _parseStrings(List strings) => strings.forEach(_strings.add);
int get accountedBytes {
int sum = 0;
for (final _NodeInfo info in _nodes.values) {
sum += info.selfSize;
}
return sum;
}
int get unknownCount {
final int unknownType = _nodeTypes.indexOf("Unknown");
Expect.isTrue(unknownType >= 0);
int count = 0;
for (final MapEntry<int, _NodeInfo> entry in _nodes.entries) {
if (entry.value.type == unknownType) {
++count;
}
}
return count;
}
bool get isEmpty => _nodes.isEmpty;
int get nodeCount => _nodes.length;
Iterable<int> get nodes => _nodes.keys;
Iterable<int> targetsOf(int source) {
return _toEdges[source].map((_EdgeInfo i) => i.nodeOffset);
}
Iterable<int> sourcesOf(int source) {
return _fromEdges[source].map((_EdgeInfo i) => i.nodeOffset);
}
int get root => 0;
NodeInfo operator [](int node) {
_NodeInfo info = _nodes[node];
final type = info.type != null ? _nodeTypes[info.type] : null;
final name = info.name != null ? _strings[info.name] : null;
return NodeInfo(type, name, info.id, info.selfSize);
}
}

View file

@ -13,7 +13,6 @@
# passed to Fasta.
set -e
set -x
OPTIONS=()
GEN_KERNEL_OPTIONS=()

View file

@ -0,0 +1,102 @@
// Copyright (c) 2018, 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.
import "dart:convert";
import "dart:io";
import "package:expect/expect.dart";
import "package:vm/v8_snapshot_profile.dart";
String path(List<String> segments) {
return "/" + segments.join("/");
}
test(bool use_elf) async {
if (Platform.isWindows) return;
final List<String> sdkBaseSegments =
Uri.file(Platform.resolvedExecutable).pathSegments.toList();
sdkBaseSegments
.replaceRange(sdkBaseSegments.indexOf("out"), sdkBaseSegments.length, []);
// Generate the snapshot profile.
final String thisTestPath = path(sdkBaseSegments) +
"/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart";
final Directory temp = await Directory.systemTemp.createTemp();
final String snapshotPath = temp.path + "/test.snap";
final List<String> precompiler2Args = [
"--write-v8-snapshot-profile-to=${temp.path}/profile.heapsnapshot",
thisTestPath,
snapshotPath,
];
if (use_elf) {
precompiler2Args.insert(0, "--build-elf");
}
final ProcessResult result = await Process.run(
"pkg/vm/tool/precompiler2",
precompiler2Args,
workingDirectory: path(sdkBaseSegments),
runInShell: true,
);
// The precompiler2 script tried using GCC for the wrong architecture. We
// don't have a workaround for this now.
if (use_elf &&
result.exitCode != 0 &&
result.stderr.contains("Assembler messages")) {
return;
}
print(precompiler2Args);
print(result.stderr);
print(result.stdout);
Expect.equals(result.exitCode, 0);
Expect.equals(result.stderr, "");
Expect.equals(result.stdout, "");
final V8SnapshotProfile profile = V8SnapshotProfile.fromJson(JsonDecoder()
.convert(File("${temp.path}/profile.heapsnapshot").readAsStringSync()));
// Verify that there are no "unknown" nodes. These are emitted when we see a
// reference to an some object but no other metadata about the object was
// recorded. We should at least record the type for every object in the graph
// (in some cases the shallow size can legitimately be 0, e.g. for "base
// objects").
for (final int node in profile.nodes) {
if (profile[node].type == "Unknown") {
print(profile[node].id);
}
Expect.notEquals(profile[node].type, "Unknown");
}
// Verify that all nodes are reachable from the declared roots.
int unreachableNodes = 0;
Set<int> nodesReachableFromRoots = profile.preOrder(profile.root).toSet();
for (final int node in profile.nodes) {
if (!nodesReachableFromRoots.contains(node)) {
++unreachableNodes;
}
}
Expect.equals(unreachableNodes, 0);
// Verify that the actual size of the snapshot is close to the sum of the
// shallow sizes of all objects in the profile. They will not be exactly equal
// because of global headers and padding.
if (use_elf) {
await Process.run("strip", [snapshotPath]);
}
final int actual = await File(snapshotPath).length();
final int expected = profile.accountedBytes;
Expect.isTrue((actual - expected).abs() / actual < 0.01);
}
main() async {
test(false);
test(true);
}

View file

@ -314,3 +314,6 @@ dart/stack_overflow_shared_test: SkipSlow # Too slow with --shared-slow-path-tri
[ $builder_tag == obfuscated && $compiler == dartkp ]
dart/optimized_stacktrace_line_and_column_test: SkipByDesign # Looks for filenames in stacktrace output
dart/optimized_stacktrace_line_test: SkipByDesign # Looks for filenames in stacktrace output
[ $compiler != dartkp || $arch == arm || $arch == arm64 ]
dart/v8_snapshot_profile_writer_test: SkipByDesign # Only relevant for AOT. Doesn't work in cross-compilation (has to run on the host).

View file

@ -118,9 +118,10 @@ class ClassSerializationCluster : public SerializationCluster {
s->WriteUnsigned(count);
for (intptr_t i = 0; i < count; i++) {
RawClass* cls = predefined_[i];
s->AssignRef(cls);
AutoTraceObject(cls);
intptr_t class_id = cls->ptr()->id_;
s->WriteCid(class_id);
s->AssignRef(cls);
}
count = objects_.length();
s->WriteUnsigned(count);
@ -142,6 +143,7 @@ class ClassSerializationCluster : public SerializationCluster {
}
void WriteClass(Serializer* s, RawClass* cls) {
AutoTraceObjectName(cls, cls->ptr()->name_);
Snapshot::Kind kind = s->kind();
RawObject** from = cls->from();
RawObject** to = cls->to_snapshot(kind);
@ -302,9 +304,10 @@ class TypeArgumentsSerializationCluster : public SerializationCluster {
s->WriteUnsigned(count);
for (intptr_t i = 0; i < count; i++) {
RawTypeArguments* type_args = objects_[i];
s->AssignRef(type_args);
AutoTraceObject(type_args);
intptr_t length = Smi::Value(type_args->ptr()->length_);
s->WriteUnsigned(length);
s->AssignRef(type_args);
}
}
@ -312,6 +315,7 @@ class TypeArgumentsSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawTypeArguments* type_args = objects_[i];
AutoTraceObject(type_args);
intptr_t length = Smi::Value(type_args->ptr()->length_);
s->WriteUnsigned(length);
s->Write<bool>(type_args->IsCanonical());
@ -400,6 +404,7 @@ class PatchClassSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawPatchClass* cls = objects_[i];
AutoTraceObject(cls);
RawObject** from = cls->from();
RawObject** to = cls->to_snapshot(s->kind());
for (RawObject** p = from; p <= to; p++) {
@ -501,6 +506,7 @@ class FunctionSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawFunction* func = objects_[i];
AutoTraceObjectName(func, func->ptr()->name_);
RawObject** from = func->from();
RawObject** to = func->to_snapshot(s->kind());
for (RawObject** p = from; p <= to; p++) {
@ -689,6 +695,7 @@ class ClosureDataSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawClosureData* data = objects_[i];
AutoTraceObject(data);
if (s->kind() != Snapshot::kFullAOT) {
s->WriteRef(data->ptr()->context_scope_);
}
@ -770,6 +777,7 @@ class SignatureDataSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawSignatureData* data = objects_[i];
AutoTraceObject(data);
RawObject** from = data->from();
RawObject** to = data->to();
for (RawObject** p = from; p <= to; p++) {
@ -847,6 +855,7 @@ class RedirectionDataSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawRedirectionData* data = objects_[i];
AutoTraceObject(data);
RawObject** from = data->from();
RawObject** to = data->to();
for (RawObject** p = from; p <= to; p++) {
@ -955,6 +964,7 @@ class FieldSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawField* field = objects_[i];
AutoTraceObject(field);
s->WriteRef(field->ptr()->name_);
s->WriteRef(field->ptr()->owner_);
@ -1112,6 +1122,7 @@ class ScriptSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawScript* script = objects_[i];
AutoTraceObject(script);
RawObject** from = script->from();
RawObject** to = script->to_snapshot(kind);
for (RawObject** p = from; p <= to; p++) {
@ -1203,6 +1214,7 @@ class LibrarySerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawLibrary* lib = objects_[i];
AutoTraceObjectName(lib, lib->ptr()->url_);
RawObject** from = lib->from();
RawObject** to = lib->to_snapshot(s->kind());
for (RawObject** p = from; p <= to; p++) {
@ -1307,6 +1319,7 @@ class NamespaceSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawNamespace* ns = objects_[i];
AutoTraceObject(ns);
RawObject** from = ns->from();
RawObject** to = ns->to();
for (RawObject** p = from; p <= to; p++) {
@ -1384,6 +1397,7 @@ class KernelProgramInfoSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawKernelProgramInfo* info = objects_[i];
AutoTraceObject(info);
RawObject** from = info->from();
RawObject** to = info->to_snapshot(s->kind());
for (RawObject** p = from; p <= to; p++) {
@ -1494,6 +1508,7 @@ class CodeSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawCode* code = objects_[i];
AutoTraceObject(code);
intptr_t pointer_offsets_length =
Code::PtrOffBits::decode(code->ptr()->state_bits_);
@ -1740,9 +1755,10 @@ class ObjectPoolSerializationCluster : public SerializationCluster {
s->WriteUnsigned(count);
for (intptr_t i = 0; i < count; i++) {
RawObjectPool* pool = objects_[i];
s->AssignRef(pool);
AutoTraceObject(pool);
intptr_t length = pool->ptr()->length_;
s->WriteUnsigned(length);
s->AssignRef(pool);
}
}
@ -1750,6 +1766,7 @@ class ObjectPoolSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawObjectPool* pool = objects_[i];
AutoTraceObject(pool);
intptr_t length = pool->ptr()->length_;
s->WriteUnsigned(length);
uint8_t* entry_bits = pool->ptr()->entry_bits();
@ -1900,12 +1917,13 @@ class RODataSerializationCluster : public SerializationCluster {
s->WriteUnsigned(count);
for (intptr_t i = 0; i < count; i++) {
RawObject* object = shared_objects_[i];
s->AssignRef(object);
AutoTraceObject(object);
uint32_t offset;
if (!s->GetSharedDataOffset(object, &offset)) {
UNREACHABLE();
}
s->WriteUnsigned(offset);
s->AssignRef(object);
}
count = objects_.length();
@ -1913,12 +1931,14 @@ class RODataSerializationCluster : public SerializationCluster {
uint32_t running_offset = 0;
for (intptr_t i = 0; i < count; i++) {
RawObject* object = objects_[i];
s->AssignRef(object);
AutoTraceObject(object);
uint32_t offset = s->GetDataOffset(object);
s->TraceDataOffset(offset);
ASSERT(Utils::IsAligned(offset, kObjectAlignment));
ASSERT(offset > running_offset);
s->WriteUnsigned((offset - running_offset) >> kObjectAlignmentLog2);
running_offset = offset;
s->AssignRef(object);
}
}
@ -1978,9 +1998,10 @@ class ExceptionHandlersSerializationCluster : public SerializationCluster {
s->WriteUnsigned(count);
for (intptr_t i = 0; i < count; i++) {
RawExceptionHandlers* handlers = objects_[i];
s->AssignRef(handlers);
AutoTraceObject(handlers);
intptr_t length = handlers->ptr()->num_entries_;
s->WriteUnsigned(length);
s->AssignRef(handlers);
}
}
@ -1988,6 +2009,7 @@ class ExceptionHandlersSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawExceptionHandlers* handlers = objects_[i];
AutoTraceObject(handlers);
intptr_t length = handlers->ptr()->num_entries_;
s->WriteUnsigned(length);
s->WriteRef(handlers->ptr()->handled_types_data_);
@ -2072,9 +2094,10 @@ class ContextSerializationCluster : public SerializationCluster {
s->WriteUnsigned(count);
for (intptr_t i = 0; i < count; i++) {
RawContext* context = objects_[i];
s->AssignRef(context);
AutoTraceObject(context);
intptr_t length = context->ptr()->num_variables_;
s->WriteUnsigned(length);
s->AssignRef(context);
}
}
@ -2082,6 +2105,7 @@ class ContextSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawContext* context = objects_[i];
AutoTraceObject(context);
intptr_t length = context->ptr()->num_variables_;
s->WriteUnsigned(length);
s->WriteRef(context->ptr()->parent_);
@ -2154,9 +2178,10 @@ class ContextScopeSerializationCluster : public SerializationCluster {
s->WriteUnsigned(count);
for (intptr_t i = 0; i < count; i++) {
RawContextScope* scope = objects_[i];
s->AssignRef(scope);
AutoTraceObject(scope);
intptr_t length = scope->ptr()->num_variables_;
s->WriteUnsigned(length);
s->AssignRef(scope);
}
}
@ -2164,6 +2189,7 @@ class ContextScopeSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawContextScope* scope = objects_[i];
AutoTraceObject(scope);
intptr_t length = scope->ptr()->num_variables_;
s->WriteUnsigned(length);
s->Write<bool>(scope->ptr()->is_implicit_);
@ -2248,6 +2274,7 @@ class UnlinkedCallSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawUnlinkedCall* unlinked = objects_[i];
AutoTraceObject(unlinked);
RawObject** from = unlinked->from();
RawObject** to = unlinked->to();
for (RawObject** p = from; p <= to; p++) {
@ -2327,6 +2354,7 @@ class ICDataSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawICData* ic = objects_[i];
AutoTraceObject(ic);
RawObject** from = ic->from();
RawObject** to = ic->to_snapshot(kind);
for (RawObject** p = from; p <= to; p++) {
@ -2420,6 +2448,7 @@ class MegamorphicCacheSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawMegamorphicCache* cache = objects_[i];
AutoTraceObject(cache);
RawObject** from = cache->from();
RawObject** to = cache->to();
for (RawObject** p = from; p <= to; p++) {
@ -2496,6 +2525,7 @@ class SubtypeTestCacheSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawSubtypeTestCache* cache = objects_[i];
AutoTraceObject(cache);
s->WriteRef(cache->ptr()->cache_);
}
}
@ -2566,6 +2596,7 @@ class LanguageErrorSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawLanguageError* error = objects_[i];
AutoTraceObject(error);
RawObject** from = error->from();
RawObject** to = error->to();
for (RawObject** p = from; p <= to; p++) {
@ -2650,6 +2681,7 @@ class UnhandledExceptionSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawUnhandledException* exception = objects_[i];
AutoTraceObject(exception);
RawObject** from = exception->from();
RawObject** to = exception->to();
for (RawObject** p = from; p <= to; p++) {
@ -2743,6 +2775,7 @@ class InstanceSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawInstance* instance = objects_[i];
AutoTraceObject(instance);
s->Write<bool>(instance->IsCanonical());
intptr_t offset = Instance::NextFieldOffset();
while (offset < next_field_offset) {
@ -2847,6 +2880,7 @@ class LibraryPrefixSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawLibraryPrefix* prefix = objects_[i];
AutoTraceObject(prefix);
RawObject** from = prefix->from();
RawObject** to = prefix->to_snapshot(kind);
for (RawObject** p = from; p <= to; p++) {
@ -2961,6 +2995,7 @@ class TypeSerializationCluster : public SerializationCluster {
intptr_t count = canonical_objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawType* type = canonical_objects_[i];
AutoTraceObject(type);
RawObject** from = type->from();
RawObject** to = type->to();
for (RawObject** p = from; p <= to; p++) {
@ -2977,6 +3012,7 @@ class TypeSerializationCluster : public SerializationCluster {
count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawType* type = objects_[i];
AutoTraceObject(type);
RawObject** from = type->from();
RawObject** to = type->to();
for (RawObject** p = from; p <= to; p++) {
@ -3144,6 +3180,7 @@ class TypeRefSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawTypeRef* type = objects_[i];
AutoTraceObject(type);
RawObject** from = type->from();
RawObject** to = type->to();
for (RawObject** p = from; p <= to; p++) {
@ -3253,6 +3290,7 @@ class TypeParameterSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawTypeParameter* type = objects_[i];
AutoTraceObject(type);
RawObject** from = type->from();
RawObject** to = type->to();
for (RawObject** p = from; p <= to; p++) {
@ -3365,6 +3403,7 @@ class BoundedTypeSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawBoundedType* type = objects_[i];
AutoTraceObject(type);
RawObject** from = type->from();
RawObject** to = type->to();
for (RawObject** p = from; p <= to; p++) {
@ -3441,6 +3480,7 @@ class ClosureSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawClosure* closure = objects_[i];
AutoTraceObject(closure);
s->Write<bool>(closure->IsCanonical());
RawObject** from = closure->from();
RawObject** to = closure->to();
@ -3510,15 +3550,17 @@ class MintSerializationCluster : public SerializationCluster {
s->WriteUnsigned(smis_.length() + mints_.length());
for (intptr_t i = 0; i < smis_.length(); i++) {
RawSmi* smi = smis_[i];
s->AssignRef(smi);
AutoTraceObject(smi);
s->Write<bool>(true);
s->Write<int64_t>(Smi::Value(smi));
s->AssignRef(smi);
}
for (intptr_t i = 0; i < mints_.length(); i++) {
RawMint* mint = mints_[i];
s->AssignRef(mint);
AutoTraceObject(mint);
s->Write<bool>(mint->IsCanonical());
s->Write<int64_t>(mint->ptr()->value_);
s->AssignRef(mint);
}
}
@ -3602,6 +3644,7 @@ class DoubleSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawDouble* dbl = objects_[i];
AutoTraceObject(dbl);
s->Write<bool>(dbl->IsCanonical());
s->Write<double>(dbl->ptr()->value_);
}
@ -3672,6 +3715,7 @@ class GrowableObjectArraySerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawGrowableObjectArray* array = objects_[i];
AutoTraceObject(array);
s->Write<bool>(array->IsCanonical());
RawObject** from = array->from();
RawObject** to = array->to();
@ -3740,9 +3784,10 @@ class TypedDataSerializationCluster : public SerializationCluster {
s->WriteUnsigned(count);
for (intptr_t i = 0; i < count; i++) {
RawTypedData* data = objects_[i];
s->AssignRef(data);
AutoTraceObject(data);
intptr_t length = Smi::Value(data->ptr()->length_);
s->WriteUnsigned(length);
s->AssignRef(data);
}
}
@ -3751,6 +3796,7 @@ class TypedDataSerializationCluster : public SerializationCluster {
intptr_t element_size = TypedData::ElementSizeInBytes(cid_);
for (intptr_t i = 0; i < count; i++) {
RawTypedData* data = objects_[i];
AutoTraceObject(data);
intptr_t length = Smi::Value(data->ptr()->length_);
s->WriteUnsigned(length);
s->Write<bool>(data->IsCanonical());
@ -3833,6 +3879,7 @@ class ExternalTypedDataSerializationCluster : public SerializationCluster {
intptr_t element_size = ExternalTypedData::ElementSizeInBytes(cid_);
for (intptr_t i = 0; i < count; i++) {
RawExternalTypedData* data = objects_[i];
AutoTraceObject(data);
intptr_t length = Smi::Value(data->ptr()->length_);
s->WriteUnsigned(length);
uint8_t* cdata = reinterpret_cast<uint8_t*>(data->ptr()->data_);
@ -3916,6 +3963,7 @@ class StackTraceSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawStackTrace* trace = objects_[i];
AutoTraceObject(trace);
RawObject** from = trace->from();
RawObject** to = trace->to();
for (RawObject** p = from; p <= to; p++) {
@ -3992,6 +4040,7 @@ class RegExpSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawRegExp* regexp = objects_[i];
AutoTraceObject(regexp);
RawObject** from = regexp->from();
RawObject** to = regexp->to();
for (RawObject** p = from; p <= to; p++) {
@ -4073,6 +4122,7 @@ class WeakPropertySerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawWeakProperty* property = objects_[i];
AutoTraceObject(property);
RawObject** from = property->from();
RawObject** to = property->to();
for (RawObject** p = from; p <= to; p++) {
@ -4159,6 +4209,7 @@ class LinkedHashMapSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawLinkedHashMap* map = objects_[i];
AutoTraceObject(map);
s->Write<bool>(map->IsCanonical());
s->WriteRef(map->ptr()->type_arguments_);
@ -4270,9 +4321,10 @@ class ArraySerializationCluster : public SerializationCluster {
s->WriteUnsigned(count);
for (intptr_t i = 0; i < count; i++) {
RawArray* array = objects_[i];
s->AssignRef(array);
AutoTraceObject(array);
intptr_t length = Smi::Value(array->ptr()->length_);
s->WriteUnsigned(length);
s->AssignRef(array);
}
}
@ -4280,6 +4332,7 @@ class ArraySerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawArray* array = objects_[i];
AutoTraceObject(array);
intptr_t length = Smi::Value(array->ptr()->length_);
s->WriteUnsigned(length);
s->Write<bool>(array->IsCanonical());
@ -4352,9 +4405,10 @@ class OneByteStringSerializationCluster : public SerializationCluster {
s->WriteUnsigned(count);
for (intptr_t i = 0; i < count; i++) {
RawOneByteString* str = objects_[i];
s->AssignRef(str);
AutoTraceObject(str);
intptr_t length = Smi::Value(str->ptr()->length_);
s->WriteUnsigned(length);
s->AssignRef(str);
}
}
@ -4362,6 +4416,7 @@ class OneByteStringSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawOneByteString* str = objects_[i];
AutoTraceObject(str);
intptr_t length = Smi::Value(str->ptr()->length_);
s->WriteUnsigned(length);
s->Write<bool>(str->IsCanonical());
@ -4429,9 +4484,10 @@ class TwoByteStringSerializationCluster : public SerializationCluster {
s->WriteUnsigned(count);
for (intptr_t i = 0; i < count; i++) {
RawTwoByteString* str = objects_[i];
s->AssignRef(str);
AutoTraceObject(str);
intptr_t length = Smi::Value(str->ptr()->length_);
s->WriteUnsigned(length);
s->AssignRef(str);
}
}
@ -4439,6 +4495,7 @@ class TwoByteStringSerializationCluster : public SerializationCluster {
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawTwoByteString* str = objects_[i];
AutoTraceObject(str);
intptr_t length = Smi::Value(str->ptr()->length_);
s->WriteUnsigned(length);
s->Write<bool>(str->IsCanonical());
@ -4508,7 +4565,9 @@ Serializer::Serializer(Thread* thread,
uint8_t** buffer,
ReAlloc alloc,
intptr_t initial_size,
ImageWriter* image_writer)
ImageWriter* image_writer,
bool vm,
V8SnapshotProfileWriter* profile_writer)
: StackResource(thread),
heap_(thread->isolate()->heap()),
zone_(thread->zone()),
@ -4520,7 +4579,9 @@ Serializer::Serializer(Thread* thread,
num_cids_(0),
num_base_objects_(0),
num_written_objects_(0),
next_ref_index_(1)
next_ref_index_(1),
vm_(vm),
profile_writer_(profile_writer)
#if defined(SNAPSHOT_BACKTRACE)
,
current_parent_(Object::null()),
@ -4538,6 +4599,43 @@ Serializer::~Serializer() {
delete[] clusters_by_cid_;
}
void Serializer::TraceStartWritingObject(const char* type,
RawObject* obj,
RawString* name) {
if (profile_writer_ == nullptr) return;
intptr_t id = 0;
if (obj->IsHeapObject()) {
id = heap_->GetObjectId(obj);
} else {
id = smi_ids_.Lookup(Smi::RawCast(obj))->id_;
}
ASSERT(id != 0);
const char* name_str = nullptr;
if (name != nullptr) {
String& str = thread()->StringHandle();
str = name;
name_str = str.ToCString();
}
object_currently_writing_id_ = id;
profile_writer_->SetObjectTypeAndName(
{V8SnapshotProfileWriter::kSnapshot, id}, type, name_str);
object_currently_writing_start_ = stream_.Position();
}
void Serializer::TraceEndWritingObject() {
if (profile_writer_ != nullptr) {
ASSERT(object_currently_writing_id_ != 0);
profile_writer_->AttributeBytesTo(
{V8SnapshotProfileWriter::kSnapshot, object_currently_writing_id_},
stream_.Position() - object_currently_writing_start_);
object_currently_writing_id_ = 0;
object_currently_writing_start_ = 0;
}
}
SerializationCluster* Serializer::NewClusterForClass(intptr_t cid) {
#if defined(DART_PRECOMPILED_RUNTIME)
UNREACHABLE();
@ -4675,6 +4773,31 @@ void Serializer::WriteInstructions(RawInstructions* instr, RawCode* code) {
const intptr_t offset = image_writer_->GetTextOffsetFor(instr, code);
ASSERT(offset != 0);
Write<int32_t>(offset);
// If offset < 0, it's pointing to a shared instruction. We don't profile
// references to shared text/data (since they don't consume any space). Of
// course, the space taken for the reference is profiled.
if (profile_writer_ != nullptr && offset >= 0) {
// Instructions cannot be roots.
ASSERT(object_currently_writing_id_ != 0);
auto offset_space = vm_ ? V8SnapshotProfileWriter::kVmText
: V8SnapshotProfileWriter::kIsolateText;
profile_writer_->AttributeReferenceTo(
{V8SnapshotProfileWriter::kSnapshot, object_currently_writing_id_},
{offset_space, offset < 0 ? -offset : offset});
}
}
void Serializer::TraceDataOffset(uint32_t offset) {
if (profile_writer_ != nullptr) {
// ROData cannot be roots.
ASSERT(object_currently_writing_id_ != 0);
auto offset_space = vm_ ? V8SnapshotProfileWriter::kVmData
: V8SnapshotProfileWriter::kIsolateData;
profile_writer_->AttributeReferenceTo(
{V8SnapshotProfileWriter::kSnapshot, object_currently_writing_id_},
{offset_space, offset});
}
}
bool Serializer::GetSharedDataOffset(RawObject* object,
@ -4919,30 +5042,36 @@ void Serializer::AddVMIsolateBaseObjects() {
// These objects are always allocated by Object::InitOnce, so they are not
// written into the snapshot.
AddBaseObject(Object::null());
AddBaseObject(Object::sentinel().raw());
AddBaseObject(Object::transition_sentinel().raw());
AddBaseObject(Object::empty_array().raw());
AddBaseObject(Object::zero_array().raw());
AddBaseObject(Object::dynamic_type().raw());
AddBaseObject(Object::void_type().raw());
AddBaseObject(Object::empty_type_arguments().raw());
AddBaseObject(Bool::True().raw());
AddBaseObject(Bool::False().raw());
AddBaseObject(Object::null(), "Null", "<null>");
AddBaseObject(Object::sentinel().raw(), "Sentinel");
AddBaseObject(Object::transition_sentinel().raw(), "Sentinel");
AddBaseObject(Object::empty_array().raw(), "Array", "<empty_array>");
AddBaseObject(Object::zero_array().raw(), "Array", "<zero_array>");
AddBaseObject(Object::dynamic_type().raw(), "Type", "<dynamic type>");
AddBaseObject(Object::void_type().raw(), "Type", "<void type>");
AddBaseObject(Object::empty_type_arguments().raw(), "TypeArguments", "[]");
AddBaseObject(Bool::True().raw(), "bool", "true");
AddBaseObject(Bool::False().raw(), "bool", "false");
ASSERT(Object::extractor_parameter_types().raw() != Object::null());
AddBaseObject(Object::extractor_parameter_types().raw());
AddBaseObject(Object::extractor_parameter_types().raw(), "Array",
"<extractor parameter types>");
ASSERT(Object::extractor_parameter_names().raw() != Object::null());
AddBaseObject(Object::extractor_parameter_names().raw());
AddBaseObject(Object::empty_context_scope().raw());
AddBaseObject(Object::empty_descriptors().raw());
AddBaseObject(Object::empty_var_descriptors().raw());
AddBaseObject(Object::empty_exception_handlers().raw());
AddBaseObject(Object::extractor_parameter_names().raw(), "Array",
"<extractor parameter names>");
AddBaseObject(Object::empty_context_scope().raw(), "ContextScope", "<empty>");
AddBaseObject(Object::empty_descriptors().raw(), "PcDescriptors", "<empty>");
AddBaseObject(Object::empty_var_descriptors().raw(), "LocalVarDescriptors",
"<empty>");
AddBaseObject(Object::empty_exception_handlers().raw(), "ExceptionHandlers",
"<empty>");
for (intptr_t i = 0; i < ArgumentsDescriptor::kCachedDescriptorCount; i++) {
AddBaseObject(ArgumentsDescriptor::cached_args_descriptors_[i]);
AddBaseObject(ArgumentsDescriptor::cached_args_descriptors_[i],
"ArgumentsDescriptor", "<cached arguments descriptor>");
}
for (intptr_t i = 0; i < ICData::kCachedICDataArrayCount; i++) {
AddBaseObject(ICData::cached_icdata_arrays_[i]);
AddBaseObject(ICData::cached_icdata_arrays_[i], "ICData",
"<cached icdata>");
}
ClassTable* table = isolate()->class_table();
@ -4950,15 +5079,15 @@ void Serializer::AddVMIsolateBaseObjects() {
// Error has no class object.
if (cid != kErrorCid) {
ASSERT(table->HasValidClassAt(cid));
AddBaseObject(table->At(cid));
AddBaseObject(table->At(cid), "Class");
}
}
AddBaseObject(table->At(kDynamicCid));
AddBaseObject(table->At(kVoidCid));
AddBaseObject(table->At(kDynamicCid), "Class");
AddBaseObject(table->At(kVoidCid), "Class");
if (!Snapshot::IncludesCode(kind_)) {
for (intptr_t i = 0; i < StubCode::NumEntries(); i++) {
AddBaseObject(StubCode::EntryAt(i).raw());
AddBaseObject(StubCode::EntryAt(i).raw(), "Code", "<stub code>");
}
}
}
@ -4985,10 +5114,10 @@ intptr_t Serializer::WriteVMSnapshot(const Array& symbols,
Serialize();
// Write roots.
WriteRef(symbols.raw());
WriteRootRef(symbols.raw());
if (Snapshot::IncludesCode(kind_)) {
for (intptr_t i = 0; i < StubCode::NumEntries(); i++) {
WriteRef(StubCode::EntryAt(i).raw());
WriteRootRef(StubCode::EntryAt(i).raw());
}
}
@ -5031,7 +5160,7 @@ void Serializer::WriteIsolateSnapshot(intptr_t num_base_objects,
// Write roots.
for (RawObject** p = from; p <= to; p++) {
WriteRef(*p);
WriteRootRef(*p);
}
#if defined(DEBUG)
@ -5567,6 +5696,13 @@ class SeedVMIsolateVisitor : public ClassVisitor, public FunctionVisitor {
KernelProgramInfo& kernel_program_info_;
};
#if defined(DART_PRECOMPILER)
DEFINE_FLAG(charp,
write_v8_snapshot_profile_to,
NULL,
"Write a snapshot profile in V8 format to a file.");
#endif
FullSnapshotWriter::FullSnapshotWriter(Snapshot::Kind kind,
uint8_t** vm_snapshot_data_buffer,
uint8_t** isolate_snapshot_data_buffer,
@ -5630,6 +5766,12 @@ FullSnapshotWriter::FullSnapshotWriter(Snapshot::Kind kind,
saved_symbol_table_ = object_store->symbol_table();
new_vm_symbol_table_ = Dart::vm_isolate()->object_store()->symbol_table();
}
#if defined(DART_PRECOMPILER)
if (FLAG_write_v8_snapshot_profile_to != nullptr) {
profile_writer_ = new (zone()) V8SnapshotProfileWriter(zone());
}
#endif
}
FullSnapshotWriter::~FullSnapshotWriter() {
@ -5647,7 +5789,8 @@ intptr_t FullSnapshotWriter::WriteVMSnapshot() {
ASSERT(vm_snapshot_data_buffer_ != NULL);
Serializer serializer(thread(), kind_, vm_snapshot_data_buffer_, alloc_,
kInitialSize, vm_image_writer_);
kInitialSize, vm_image_writer_, /*vm=*/true,
profile_writer_);
serializer.ReserveHeader();
serializer.WriteVersionAndFeatures(true);
@ -5661,10 +5804,12 @@ intptr_t FullSnapshotWriter::WriteVMSnapshot() {
clustered_vm_size_ = serializer.bytes_written();
if (Snapshot::IncludesCode(kind_)) {
vm_image_writer_->SetProfileWriter(profile_writer_);
vm_image_writer_->Write(serializer.stream(), true);
mapped_data_size_ += vm_image_writer_->data_size();
mapped_text_size_ += vm_image_writer_->text_size();
vm_image_writer_->ResetOffsets();
vm_image_writer_->ClearProfileWriter();
}
// The clustered part + the direct mapped data part.
@ -5677,7 +5822,8 @@ void FullSnapshotWriter::WriteIsolateSnapshot(intptr_t num_base_objects) {
thread(), Timeline::GetIsolateStream(), "WriteIsolateSnapshot"));
Serializer serializer(thread(), kind_, isolate_snapshot_data_buffer_, alloc_,
kInitialSize, isolate_image_writer_);
kInitialSize, isolate_image_writer_, /*vm=*/false,
profile_writer_);
ObjectStore* object_store = isolate()->object_store();
ASSERT(object_store != NULL);
@ -5690,6 +5836,7 @@ void FullSnapshotWriter::WriteIsolateSnapshot(intptr_t num_base_objects) {
clustered_isolate_size_ = serializer.bytes_written();
if (Snapshot::IncludesCode(kind_)) {
isolate_image_writer_->SetProfileWriter(profile_writer_);
isolate_image_writer_->Write(serializer.stream(), false);
#if defined(DART_PRECOMPILER)
isolate_image_writer_->DumpStatistics();
@ -5698,6 +5845,7 @@ void FullSnapshotWriter::WriteIsolateSnapshot(intptr_t num_base_objects) {
mapped_data_size_ += isolate_image_writer_->data_size();
mapped_text_size_ += isolate_image_writer_->text_size();
isolate_image_writer_->ResetOffsets();
isolate_image_writer_->ClearProfileWriter();
}
// The clustered part + the direct mapped data part.
@ -5726,6 +5874,12 @@ void FullSnapshotWriter::WriteFullSnapshot() {
clustered_vm_size_ + clustered_isolate_size_ + mapped_data_size_ +
mapped_text_size_);
}
#if defined(DART_PRECOMPILER)
if (FLAG_write_v8_snapshot_profile_to != nullptr) {
profile_writer_->Write(FLAG_write_v8_snapshot_profile_to);
}
#endif
}
FullSnapshotReader::FullSnapshotReader(const Snapshot* snapshot,

View file

@ -16,6 +16,7 @@
#include "vm/object.h"
#include "vm/snapshot.h"
#include "vm/type_testing_stubs.h"
#include "vm/v8_snapshot_writer.h"
#include "vm/version.h"
#if defined(DEBUG)
@ -132,7 +133,9 @@ class Serializer : public StackResource {
uint8_t** buffer,
ReAlloc alloc,
intptr_t initial_size,
ImageWriter* image_writer_);
ImageWriter* image_writer_,
bool vm_,
V8SnapshotProfileWriter* profile_writer = nullptr);
~Serializer();
intptr_t WriteVMSnapshot(const Array& symbols,
@ -142,12 +145,26 @@ class Serializer : public StackResource {
void AddVMIsolateBaseObjects();
void AddBaseObject(RawObject* base_object) {
AssignRef(base_object);
void AddBaseObject(RawObject* base_object,
const char* type = nullptr,
const char* name = nullptr) {
intptr_t ref = AssignRef(base_object);
num_base_objects_++;
if (profile_writer_ != nullptr) {
if (type == nullptr) {
type = "Unknown";
}
if (name == nullptr) {
name = "<base object>";
}
profile_writer_->SetObjectTypeAndName(
{V8SnapshotProfileWriter::kSnapshot, ref}, type, name);
profile_writer_->AddRoot({V8SnapshotProfileWriter::kSnapshot, ref});
}
}
void AssignRef(RawObject* object) {
intptr_t AssignRef(RawObject* object) {
ASSERT(next_ref_index_ != 0);
if (object->IsHeapObject()) {
// The object id weak table holds image offsets for Instructions instead
@ -168,7 +185,7 @@ class Serializer : public StackResource {
smi_ids_.Insert(new_pair);
}
}
next_ref_index_++;
return next_ref_index_++;
}
void Push(RawObject* object);
@ -202,6 +219,11 @@ class Serializer : public StackResource {
WriteStream* stream() { return &stream_; }
intptr_t bytes_written() { return stream_.bytes_written(); }
void TraceStartWritingObject(const char* type,
RawObject* obj,
RawString* name);
void TraceEndWritingObject();
// Writes raw data to the stream (basic type).
// sizeof(T) must be in {1,2,4,8}.
template <typename T>
@ -214,41 +236,56 @@ class Serializer : public StackResource {
}
void Align(intptr_t alignment) { stream_.Align(alignment); }
void WriteRef(RawObject* object) {
void WriteRef(RawObject* object, bool is_root = false) {
intptr_t id = 0;
if (!object->IsHeapObject()) {
RawSmi* smi = Smi::RawCast(object);
intptr_t id = smi_ids_.Lookup(smi)->id_;
id = smi_ids_.Lookup(smi)->id_;
if (id == 0) {
FATAL("Missing ref");
}
WriteUnsigned(id);
return;
} else {
// The object id weak table holds image offsets for Instructions instead
// of ref indices.
ASSERT(!object->IsInstructions());
id = heap_->GetObjectId(object);
if (id == 0) {
if (object->IsCode() && !Snapshot::IncludesCode(kind_)) {
WriteRef(Object::null());
return;
}
#if !defined(DART_PRECOMPILED_RUNTIME)
if (object->IsBytecode() && !Snapshot::IncludesBytecode(kind_)) {
WriteRef(Object::null());
return;
}
#endif // !DART_PRECOMPILED_RUNTIME
if (object->IsSendPort()) {
// TODO(rmacnak): Do a better job of resetting fields in
// precompilation and assert this is unreachable.
WriteRef(Object::null());
return;
}
FATAL("Missing ref");
}
}
// The object id weak table holds image offsets for Instructions instead
// of ref indices.
ASSERT(!object->IsInstructions());
intptr_t id = heap_->GetObjectId(object);
if (id == 0) {
if (object->IsCode() && !Snapshot::IncludesCode(kind_)) {
WriteRef(Object::null());
return;
}
#if !defined(DART_PRECOMPILED_RUNTIME)
if (object->IsBytecode() && !Snapshot::IncludesBytecode(kind_)) {
WriteRef(Object::null());
return;
}
#endif // !DART_PRECOMPILED_RUNTIME
if (object->IsSendPort()) {
// TODO(rmacnak): Do a better job of resetting fields in precompilation
// and assert this is unreachable.
WriteRef(Object::null());
return;
}
FATAL("Missing ref");
}
WriteUnsigned(id);
if (profile_writer_ != nullptr) {
if (object_currently_writing_id_ != 0) {
profile_writer_->AttributeReferenceTo(
{V8SnapshotProfileWriter::kSnapshot, object_currently_writing_id_},
{V8SnapshotProfileWriter::kSnapshot, id});
} else {
ASSERT(is_root);
profile_writer_->AddRoot({V8SnapshotProfileWriter::kSnapshot, id});
}
}
}
void WriteRootRef(RawObject* object) {
WriteRef(object, /* is_root = */ true);
}
void WriteTokenPosition(TokenPosition pos) {
@ -263,6 +300,7 @@ class Serializer : public StackResource {
void WriteInstructions(RawInstructions* instr, RawCode* code);
bool GetSharedDataOffset(RawObject* object, uint32_t* offset) const;
uint32_t GetDataOffset(RawObject* object) const;
void TraceDataOffset(uint32_t offset);
intptr_t GetDataSize() const;
intptr_t GetTextSize() const;
@ -286,6 +324,13 @@ class Serializer : public StackResource {
intptr_t next_ref_index_;
SmiObjectIdMap smi_ids_;
// True if writing VM snapshot, false for Isolate snapshot.
bool vm_;
V8SnapshotProfileWriter* profile_writer_ = nullptr;
intptr_t object_currently_writing_id_ = 0;
intptr_t object_currently_writing_start_ = 0;
#if defined(SNAPSHOT_BACKTRACE)
RawObject* current_parent_;
GrowableArray<Object*> parent_pairs_;
@ -294,6 +339,27 @@ class Serializer : public StackResource {
DISALLOW_IMPLICIT_CONSTRUCTORS(Serializer);
};
struct SerializerWritingObjectScope {
SerializerWritingObjectScope(Serializer* serializer,
const char* type,
RawObject* object,
RawString* name)
: serializer_(serializer) {
serializer_->TraceStartWritingObject(type, object, name);
}
~SerializerWritingObjectScope() { serializer_->TraceEndWritingObject(); }
private:
Serializer* serializer_;
};
#define AutoTraceObject(obj) \
SerializerWritingObjectScope scope_##__COUNTER__(s, name(), obj, nullptr)
#define AutoTraceObjectName(obj, str) \
SerializerWritingObjectScope scope_##__COUNTER__(s, name(), obj, str)
class Deserializer : public StackResource {
public:
Deserializer(Thread* thread,
@ -446,6 +512,8 @@ class FullSnapshotWriter {
intptr_t mapped_data_size_;
intptr_t mapped_text_size_;
V8SnapshotProfileWriter* profile_writer_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(FullSnapshotWriter);
};

View file

@ -268,8 +268,12 @@ void ImageWriter::Write(WriteStream* clustered_stream, bool vm) {
}
// Append the direct-mapped RO data objects after the clustered snapshot.
offset_space_ = vm ? V8SnapshotProfileWriter::kVmData
: V8SnapshotProfileWriter::kIsolateData;
WriteROData(clustered_stream);
offset_space_ = vm ? V8SnapshotProfileWriter::kVmText
: V8SnapshotProfileWriter::kIsolateText;
WriteText(clustered_stream, vm);
}
@ -278,14 +282,19 @@ void ImageWriter::WriteROData(WriteStream* stream) {
// Heap page starts here.
intptr_t section_start = stream->Position();
stream->WriteWord(next_data_offset_); // Data length.
COMPILE_ASSERT(OS::kMaxPreferredCodeAlignment >= kObjectAlignment);
stream->Align(OS::kMaxPreferredCodeAlignment);
ASSERT(stream->Position() - section_start == Image::kHeaderSize);
// Heap page objects start here.
for (intptr_t i = 0; i < objects_.length(); i++) {
const Object& obj = *objects_[i].obj_;
AutoTraceImage(section_start, stream, "ROData");
NoSafepointScope no_safepoint;
uword start = reinterpret_cast<uword>(obj.raw()) - kHeapObjectTag;
@ -370,10 +379,21 @@ void AssemblyImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
ObjectStore* object_store = Isolate::Current()->object_store();
TypeTestingStubFinder tts;
intptr_t offset = Image::kHeaderSize;
for (intptr_t i = 0; i < instructions_.length(); i++) {
const Instructions& insns = *instructions_[i].insns_;
const Code& code = *instructions_[i].code_;
if (profile_writer_ != nullptr) {
ASSERT(offset_space_ != V8SnapshotProfileWriter::kSnapshot);
profile_writer_->SetObjectTypeAndName({offset_space_, offset},
"Instructions",
/*name=*/nullptr);
profile_writer_->AttributeBytesTo({offset_space_, offset},
insns.raw()->Size());
}
offset += insns.raw()->Size();
ASSERT(insns.raw()->Size() % sizeof(uint64_t) == 0);
// 1. Write from the header to the entry point.
@ -595,6 +615,7 @@ void BlobImageWriter::WriteText(WriteStream* clustered_stream, bool vm) {
NoSafepointScope no_safepoint;
for (intptr_t i = 0; i < instructions_.length(); i++) {
const Instructions& insns = *instructions_[i].insns_;
AutoTraceImage(0, &this->instructions_blob_stream_, "Instructions");
uword beginning = reinterpret_cast<uword>(insns.raw_ptr());
uword entry = beginning + Instructions::HeaderSize();

View file

@ -5,12 +5,15 @@
#ifndef RUNTIME_VM_IMAGE_SNAPSHOT_H_
#define RUNTIME_VM_IMAGE_SNAPSHOT_H_
#include <utility>
#include "platform/assert.h"
#include "vm/allocation.h"
#include "vm/datastream.h"
#include "vm/globals.h"
#include "vm/growable_array.h"
#include "vm/hash_map.h"
#include "vm/v8_snapshot_writer.h"
namespace dart {
@ -120,6 +123,14 @@ class ImageWriter : public ValueObject {
void DumpStatistics();
void SetProfileWriter(V8SnapshotProfileWriter* profile_writer) {
profile_writer_ = profile_writer;
}
void ClearProfileWriter() { profile_writer_ = nullptr; }
void TraceInstructions(const Instructions& instructions);
protected:
void WriteROData(WriteStream* stream);
virtual void WriteText(WriteStream* clustered_stream, bool vm) = 0;
@ -162,10 +173,56 @@ class ImageWriter : public ValueObject {
ObjectOffsetMap shared_instructions_;
ObjectOffsetMap reuse_instructions_;
V8SnapshotProfileWriter::IdSpace offset_space_ =
V8SnapshotProfileWriter::kSnapshot;
V8SnapshotProfileWriter* profile_writer_ = nullptr;
template <class T>
friend class TraceImageObjectScope;
private:
DISALLOW_COPY_AND_ASSIGN(ImageWriter);
};
#define AutoTraceImage(section_offset, stream, type_name) \
auto AutoTraceImagObjectScopeVar##__COUNTER__ = \
TraceImageObjectScope<std::remove_pointer<decltype(stream)>::type>( \
this, section_offset, stream, type_name);
template <typename T>
class TraceImageObjectScope {
public:
TraceImageObjectScope(ImageWriter* writer,
intptr_t section_offset,
const T* stream,
const char* type)
: writer_(writer),
stream_(stream),
section_offset_(section_offset),
start_offset_(stream_->Position() - section_offset) {
if (writer_->profile_writer_ != nullptr) {
ASSERT(writer_->offset_space_ != V8SnapshotProfileWriter::kSnapshot);
writer_->profile_writer_->SetObjectTypeAndName(
{writer_->offset_space_, start_offset_}, type, nullptr);
}
}
~TraceImageObjectScope() {
if (writer_->profile_writer_ != nullptr) {
ASSERT(writer_->offset_space_ != V8SnapshotProfileWriter::kSnapshot);
writer_->profile_writer_->AttributeBytesTo(
{writer_->offset_space_, start_offset_},
stream_->Position() - section_offset_ - start_offset_);
}
}
private:
ImageWriter* writer_;
const T* stream_;
intptr_t section_offset_;
intptr_t start_offset_;
};
class AssemblyImageWriter : public ImageWriter {
public:
AssemblyImageWriter(Thread* thread,

View file

@ -298,6 +298,10 @@ void JSONWriter::PrintPropertyName(const char* name) {
buffer_.AddChar(':');
}
void JSONWriter::PrintNewline() {
buffer_.AddChar('\n');
}
void JSONWriter::PrintCommaIfNeeded() {
if (NeedComma()) {
buffer_.AddChar(',');

View file

@ -74,6 +74,8 @@ class JSONWriter : ValueObject {
void PrintPropertyName(const char* name);
void PrintNewline();
void AddEscapedUTF8String(const char* s);
void AddEscapedUTF8String(const char* s, intptr_t len);

View file

@ -4456,6 +4456,7 @@ class Instructions : public Object {
friend class Code;
friend class AssemblyImageWriter;
friend class BlobImageWriter;
friend class ImageWriter;
};
class LocalVarDescriptors : public Object {

View file

@ -0,0 +1,289 @@
// Copyright (c) 2018, 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.
#if defined(DART_PRECOMPILER)
#include "vm/v8_snapshot_writer.h"
#include "vm/dart.h"
#include "vm/os.h"
namespace dart {
const char* ZoneString(Zone* Z, const char* str) {
const intptr_t len = strlen(str) + 1;
char* dest = Z->Alloc<char>(len);
snprintf(dest, len, "%s", str);
return dest;
}
V8SnapshotProfileWriter::V8SnapshotProfileWriter(Zone* zone)
: zone_(zone),
node_types_(zone_),
edge_types_(zone_),
strings_(zone),
roots_(zone_, 100) {
node_types_.Insert({"Unknown", kUnknown});
node_types_.Insert({"ArtificialRoot", kArtificialRoot});
edge_types_.Insert({"context", kContext});
edge_types_.Insert({"element", kElement});
edge_types_.Insert({"property", kProperty});
edge_types_.Insert({"internal", kInternal});
edge_types_.Insert({"hidden", kHidden});
edge_types_.Insert({"shortcut", kShortcut});
edge_types_.Insert({"weak", kWeak});
edge_types_.Insert({"extra", kExtra});
strings_.Insert({"<unknown>", kUnknownString});
strings_.Insert({"<object>", kObjectString});
strings_.Insert({"<property>", kPropertyString});
strings_.Insert({"<artificial root>", kArtificialRootString});
}
void V8SnapshotProfileWriter::SetObjectTypeAndName(ObjectId object_id,
const char* type,
const char* name) {
ASSERT(type != nullptr);
NodeInfo* info = EnsureId(object_id);
if (!node_types_.HasKey(type)) {
node_types_.Insert({ZoneString(zone_, type), node_types_.Size()});
}
intptr_t type_id = node_types_.LookupValue(type);
ASSERT(info->type == kUnknown || info->type == type_id);
info->type = type_id;
if (name != nullptr) {
info->name = EnsureString(name);
} else {
info->name = EnsureString(type);
}
}
void V8SnapshotProfileWriter::AttributeBytesTo(ObjectId object_id,
size_t num_bytes) {
EnsureId(object_id)->self_size += num_bytes;
}
void V8SnapshotProfileWriter::AttributeReferenceTo(ObjectId object_id,
ObjectId to_object_id) {
EnsureId(to_object_id);
NodeInfo* info = EnsureId(object_id);
#if defined(DEBUG)
// We should never add a reference twice.
for (intptr_t i = 0; i < info->edges->length(); ++i) {
ASSERT(info->edges->At(i).to_node != object_id);
}
#endif
info->edges->Add(EdgeInfo{to_object_id});
++edge_count_;
}
V8SnapshotProfileWriter::NodeInfo V8SnapshotProfileWriter::DefaultNode(
ObjectId object_id) {
return {
kUnknown,
kUnknownString,
object_id,
0,
new (zone_) ZoneGrowableArray<EdgeInfo>(zone_, 0),
};
}
V8SnapshotProfileWriter::NodeInfo V8SnapshotProfileWriter::ArtificialRoot() {
return {
kArtificialRoot, kArtificialRootString, {kArtificial, 0}, 0, nullptr, 0,
};
}
V8SnapshotProfileWriter::NodeInfo* V8SnapshotProfileWriter::EnsureId(
ObjectId object_id) {
if (!nodes_.HasKey(object_id)) {
NodeInfo info = DefaultNode(object_id);
nodes_.Insert({object_id, info});
}
return &nodes_.Lookup(object_id)->value;
}
intptr_t V8SnapshotProfileWriter::EnsureString(const char* str) {
if (!strings_.HasKey(str)) {
strings_.Insert({ZoneString(zone_, str), strings_.Size()});
return strings_.Size() - 1;
}
return strings_.LookupValue(str);
}
void V8SnapshotProfileWriter::WriteNodeInfo(JSONWriter* writer,
const NodeInfo& info) {
writer->PrintValue(info.type);
writer->PrintValue(info.name);
writer->PrintValue(NodeIdFor(info.id));
writer->PrintValue(info.self_size);
// The artificial root has 'nullptr' edges, it actually points to all the
// roots.
writer->PrintValue64(info.edges != nullptr ? info.edges->length()
: roots_.length());
writer->PrintNewline();
}
void V8SnapshotProfileWriter::WriteEdgeInfo(JSONWriter* writer,
const EdgeInfo& info) {
writer->PrintValue64(kProperty); // type, not really used atm
writer->PrintValue64(
kPropertyString); // name_or_index, not really used either
writer->PrintValue64(nodes_.LookupValue(info.to_node).offset);
writer->PrintNewline();
}
void V8SnapshotProfileWriter::AddRoot(ObjectId object_id) {
EnsureId(object_id);
roots_.Add(object_id);
}
void V8SnapshotProfileWriter::WriteStringsTable(
JSONWriter* writer,
const DirectChainedHashMap<StringToIntMapTraits>& map) {
const char** strings = zone_->Alloc<const char*>(map.Size());
StringToIntMapTraits::Pair* pair = nullptr;
auto it = map.GetIterator();
while ((pair = it.Next()) != nullptr) {
ASSERT(pair->value >= 0 && pair->value < map.Size());
strings[pair->value] = pair->key;
}
for (intptr_t i = 0; i < map.Size(); ++i) {
writer->PrintValue(strings[i]);
writer->PrintNewline();
}
}
void V8SnapshotProfileWriter::Write(JSONWriter* writer) {
writer->OpenObject();
writer->OpenObject("snapshot");
{
writer->OpenObject("meta");
{
writer->OpenArray("node_fields");
writer->PrintValue("type");
writer->PrintValue("name");
writer->PrintValue("id");
writer->PrintValue("self_size");
writer->PrintValue("edge_count");
writer->CloseArray();
}
{
writer->OpenArray("node_types");
{
writer->OpenArray();
WriteStringsTable(writer, node_types_);
writer->CloseArray();
}
writer->CloseArray();
}
{
writer->OpenArray("edge_fields");
writer->PrintValue("type");
writer->PrintValue("name_or_index");
writer->PrintValue("to_node");
writer->CloseArray();
}
{
writer->OpenArray("edge_types");
{
writer->OpenArray();
WriteStringsTable(writer, edge_types_);
writer->CloseArray();
}
writer->CloseArray();
}
writer->CloseObject();
writer->PrintProperty64("node_count",
nodes_.Size() + 1 /* artificial root */);
writer->PrintProperty64("edge_count", edge_count_ + roots_.length());
}
writer->CloseObject();
{
writer->OpenArray("nodes");
// Write the artificial root node.
WriteNodeInfo(writer, ArtificialRoot());
intptr_t offset = kNumNodeFields;
ObjectIdToNodeInfoTraits::Pair* entry = nullptr;
auto it = nodes_.GetIterator();
while ((entry = it.Next()) != nullptr) {
ASSERT(entry->key == entry->value.id);
entry->value.offset = offset;
WriteNodeInfo(writer, entry->value);
offset += kNumNodeFields;
}
writer->CloseArray();
}
{
writer->OpenArray("edges");
// Write references from the artificial root to the actual roots.
for (ObjectId root : roots_) {
WriteEdgeInfo(writer, {root});
}
ObjectIdToNodeInfoTraits::Pair* entry = nullptr;
auto it = nodes_.GetIterator();
while ((entry = it.Next()) != nullptr) {
for (intptr_t i = 0; i < entry->value.edges->length(); ++i) {
WriteEdgeInfo(writer, entry->value.edges->At(i));
}
}
writer->CloseArray();
}
{
writer->OpenArray("strings");
WriteStringsTable(writer, strings_);
writer->CloseArray();
}
writer->CloseObject();
}
void V8SnapshotProfileWriter::Write(const char* filename) {
JSONWriter json;
Write(&json);
auto file_open = Dart::file_open_callback();
auto file_write = Dart::file_write_callback();
auto file_close = Dart::file_close_callback();
if ((file_open == nullptr) || (file_write == nullptr) ||
(file_close == nullptr)) {
OS::PrintErr("Could not access file callbacks to write snapshot profile.");
return;
}
auto file = file_open(filename, /*write=*/true);
if (file == nullptr) {
OS::PrintErr("Failed to open file %s\n", filename);
} else {
char* output = nullptr;
intptr_t output_length = 0;
json.Steal(&output, &output_length);
file_write(output, output_length, file);
free(output);
file_close(file);
}
}
} // namespace dart
#endif

View file

@ -0,0 +1,187 @@
// Copyright (c) 2018, 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.
#ifndef RUNTIME_VM_V8_SNAPSHOT_WRITER_H_
#define RUNTIME_VM_V8_SNAPSHOT_WRITER_H_
#include <utility>
#include "platform/assert.h"
#include "vm/allocation.h"
#include "vm/hash_map.h"
#include "vm/json_writer.h"
namespace dart {
struct StringToIntMapTraits {
typedef char const* Key;
typedef intptr_t Value;
struct Pair {
Key key;
Value value;
Pair() : key(nullptr), value(-1) {}
Pair(Key k, Value v) : key(k), value(v) {}
};
static Value ValueOf(Pair pair) { return pair.value; }
static Key KeyOf(Pair pair) { return pair.key; }
static size_t Hashcode(Key key) {
/// SAMIR_TODO
return 0;
}
static bool IsKeyEqual(Pair x, Key y) { return strcmp(x.key, y) == 0; }
};
class V8SnapshotProfileWriter : public ZoneAllocated {
public:
enum IdSpace {
kSnapshot = 0, // Can be VM or Isolate heap, they share ids.
kVmText = 1,
kIsolateText = 2,
kVmData = 3,
kIsolateData = 4,
kArtificial = 5, // Artificial objects (e.g. the global root).
kIdSpaceBits = 3,
};
typedef std::pair<IdSpace, intptr_t> ObjectId;
#if !defined(DART_PRECOMPILER)
explicit V8SnapshotProfileWriter(Zone* zone) {}
virtual ~V8SnapshotProfileWriter() {}
void SetObjectTypeAndName(ObjectId object_id,
const char* type,
const char* name) {}
void AttributeBytesTo(ObjectId object_id, size_t num_bytes) {}
void AttributeReferenceTo(ObjectId object_id, ObjectId to_object_id) {}
void AddRoot(ObjectId object_id) {}
#else
explicit V8SnapshotProfileWriter(Zone* zone);
virtual ~V8SnapshotProfileWriter() {}
// Records that the object referenced by 'object_id' has type 'type'. The
// 'type' for all 'Instance's should be 'Instance', not the user-visible type
// and use 'name' for the real type instead.
void SetObjectTypeAndName(ObjectId object_id,
const char* type,
const char* name);
// Charges 'num_bytes'-many bytes to 'object_id'. In a clustered snapshot,
// objects can have their data spread across multiple sections, so this can be
// called multiple times for the same object.
void AttributeBytesTo(ObjectId object_id, size_t num_bytes);
// Records that a reference to the object with id 'to_object_id' was written
// in order to serialize the object with id 'object_id'. This does not affect
// the number of bytes charged to 'object_id'.
void AttributeReferenceTo(ObjectId object_id, ObjectId to_object_id);
// Marks an object as being a root in the graph. Used for analysis of the
// graph.
void AddRoot(ObjectId object_id);
// Write to a file in the V8 Snapshot Profile (JSON/.heapsnapshot) format.
void Write(const char* file);
private:
static constexpr intptr_t kNumNodeFields = 5;
static constexpr intptr_t kNumEdgeFields = 3;
struct EdgeInfo {
// 'type' and 'name_or_index' aren't supported yet.
ObjectId to_node;
};
struct NodeInfo {
intptr_t type;
intptr_t name;
ObjectId id;
intptr_t self_size;
ZoneGrowableArray<EdgeInfo>* edges = nullptr;
// Populated during serialization.
intptr_t offset = -1;
// 'trace_node_id' isn't supported.
// 'edge_count' is computed on-demand.
// Used for testing sentinel in the hashtable.
bool operator!=(const NodeInfo& other) { return id != other.id; }
bool operator==(const NodeInfo& other) { return !(*this != other); }
};
NodeInfo DefaultNode(ObjectId object_id);
static NodeInfo ArtificialRoot();
NodeInfo* EnsureId(ObjectId object_id);
intptr_t EnsureString(const char* str);
static intptr_t NodeIdFor(ObjectId id) {
return (id.second << kIdSpaceBits) | id.first;
}
enum ConstantStrings {
kUnknownString = 0,
kPropertyString = 1,
kObjectString = 2,
kArtificialRootString = 3,
};
enum ConstantEdgeTypes {
kContext = 0,
kElement = 1,
kProperty = 2,
kInternal = 3,
kHidden = 4,
kShortcut = 5,
kWeak = 6,
kExtra = 7,
};
enum ConstantNodeTypes {
kUnknown = 0,
kArtificialRoot = 1,
};
struct ObjectIdToNodeInfoTraits {
typedef ObjectId Key;
typedef NodeInfo Value;
struct Pair {
Key key;
Value value;
Pair() : key{kSnapshot, -1}, value{0, 0, {kSnapshot, -1}, 0, nullptr} {};
Pair(Key k, Value v) : key(k), value(v) {}
};
static Key KeyOf(const Pair& pair) { return pair.key; }
static Value ValueOf(const Pair& pair) { return pair.value; }
static size_t Hashcode(Key key) { return NodeIdFor(key); }
static bool IsKeyEqual(const Pair& x, Key y) { return x.key == y; }
};
Zone* zone_;
void Write(JSONWriter* writer);
void WriteNodeInfo(JSONWriter* writer, const NodeInfo& info);
void WriteEdgeInfo(JSONWriter* writer, const EdgeInfo& info);
void WriteStringsTable(JSONWriter* writer,
const DirectChainedHashMap<StringToIntMapTraits>& map);
DirectChainedHashMap<ObjectIdToNodeInfoTraits> nodes_;
DirectChainedHashMap<StringToIntMapTraits> node_types_;
DirectChainedHashMap<StringToIntMapTraits> edge_types_;
DirectChainedHashMap<StringToIntMapTraits> strings_;
ZoneGrowableArray<ObjectId> roots_;
size_t edge_count_ = 0;
#endif
};
} // namespace dart
#endif // RUNTIME_VM_V8_SNAPSHOT_WRITER_H_

View file

@ -333,6 +333,8 @@ vm_sources = [
"unicode_data.cc",
"uri.cc",
"uri.h",
"v8_snapshot_writer.cc",
"v8_snapshot_writer.h",
"virtual_memory.cc",
"virtual_memory.h",
"virtual_memory_android.cc",