mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
![Ryan Macnak](/assets/img/avatar_default.png)
Also add some missing va_end. TEST=ci Bug: https://github.com/dart-lang/sdk/issues/47939 Change-Id: Iadfbf72493d987dfb1d102922db1b353249a4728 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/224240 Reviewed-by: Alexander Aprelev <aam@google.com> Commit-Queue: Ryan Macnak <rmacnak@google.com>
436 lines
14 KiB
C++
436 lines
14 KiB
C++
// 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.
|
|
|
|
#include "vm/v8_snapshot_writer.h"
|
|
|
|
#include "vm/dart.h"
|
|
#include "vm/os.h"
|
|
|
|
namespace dart {
|
|
|
|
const V8SnapshotProfileWriter::ObjectId
|
|
V8SnapshotProfileWriter::kArtificialRootId{IdSpace::kArtificial, 0};
|
|
|
|
#if defined(DART_PRECOMPILER)
|
|
|
|
V8SnapshotProfileWriter::V8SnapshotProfileWriter(Zone* zone)
|
|
: zone_(zone),
|
|
nodes_(zone_),
|
|
node_types_(zone_),
|
|
edge_types_(zone_),
|
|
strings_(zone_),
|
|
roots_(zone_) {
|
|
intptr_t idx = edge_types_.Add("context");
|
|
ASSERT_EQUAL(idx, static_cast<intptr_t>(Edge::Type::kContext));
|
|
idx = edge_types_.Add("element");
|
|
ASSERT_EQUAL(idx, static_cast<intptr_t>(Edge::Type::kElement));
|
|
idx = edge_types_.Add("property");
|
|
ASSERT_EQUAL(idx, static_cast<intptr_t>(Edge::Type::kProperty));
|
|
idx = edge_types_.Add("internal");
|
|
ASSERT_EQUAL(idx, static_cast<intptr_t>(Edge::Type::kInternal));
|
|
|
|
SetObjectTypeAndName(kArtificialRootId, "ArtificialRoot",
|
|
"<artificial root>");
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::SetObjectTypeAndName(const ObjectId& object_id,
|
|
const char* type,
|
|
const char* name) {
|
|
ASSERT(type != nullptr);
|
|
NodeInfo* info = EnsureId(object_id);
|
|
const intptr_t type_index = node_types_.Add(type);
|
|
if (info->type != kInvalidString && info->type != type_index) {
|
|
FATAL("Attempting to assign mismatching type %s to node %s", type,
|
|
info->ToCString(this, zone_));
|
|
}
|
|
info->type = type_index;
|
|
// Don't overwrite any existing name.
|
|
if (info->name == kInvalidString) {
|
|
info->name = strings_.Add(name);
|
|
}
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::AttributeBytesTo(const ObjectId& object_id,
|
|
size_t num_bytes) {
|
|
EnsureId(object_id)->self_size += num_bytes;
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::AttributeReferenceTo(
|
|
const ObjectId& from_object_id,
|
|
const Reference& reference,
|
|
const ObjectId& to_object_id) {
|
|
ASSERT(reference.IsElement() ? reference.offset >= 0
|
|
: reference.name != nullptr);
|
|
EnsureId(to_object_id);
|
|
const Edge edge(this, reference);
|
|
EnsureId(from_object_id)->AddEdge(edge, to_object_id);
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::AttributeDroppedReferenceTo(
|
|
const ObjectId& from_object_id,
|
|
const Reference& reference,
|
|
const ObjectId& to_object_id,
|
|
const ObjectId& replacement_object_id) {
|
|
ASSERT(to_object_id.IsArtificial());
|
|
ASSERT(!replacement_object_id.IsArtificial());
|
|
ASSERT(reference.IsElement() ? reference.offset >= 0
|
|
: reference.name != nullptr);
|
|
|
|
// The target node is added normally.
|
|
AttributeReferenceTo(from_object_id, reference, to_object_id);
|
|
|
|
EnsureId(replacement_object_id);
|
|
// Put the replacement node at an invalid offset or name that can still be
|
|
// associated with the real one. For offsets, this is the negative offset.
|
|
// For names, it's the name prefixed with ":replacement_".
|
|
Reference replacement_reference =
|
|
reference.IsElement() ? Reference::Element(-reference.offset)
|
|
: Reference::Property(OS::SCreate(
|
|
zone_, ":replacement_%s", reference.name));
|
|
const Edge replacement_edge(this, replacement_reference);
|
|
EnsureId(from_object_id)->AddEdge(replacement_edge, replacement_object_id);
|
|
}
|
|
|
|
bool V8SnapshotProfileWriter::HasId(const ObjectId& object_id) {
|
|
return nodes_.HasKey(object_id);
|
|
}
|
|
|
|
V8SnapshotProfileWriter::NodeInfo* V8SnapshotProfileWriter::EnsureId(
|
|
const ObjectId& object_id) {
|
|
if (!HasId(object_id)) {
|
|
nodes_.Insert(NodeInfo(this, object_id));
|
|
}
|
|
return nodes_.Lookup(object_id);
|
|
}
|
|
|
|
const char* V8SnapshotProfileWriter::NodeInfo::ToCString(
|
|
V8SnapshotProfileWriter* profile_writer,
|
|
Zone* zone) const {
|
|
JSONWriter writer;
|
|
WriteDebug(profile_writer, &writer);
|
|
return OS::SCreate(zone, "%s", writer.buffer()->buffer());
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::NodeInfo::Write(
|
|
V8SnapshotProfileWriter* profile_writer,
|
|
JSONWriter* writer) const {
|
|
ASSERT(id.space() != IdSpace::kInvalid);
|
|
if (type == kInvalidString) {
|
|
FATAL("No type given for node %s", id.ToCString(profile_writer->zone_));
|
|
}
|
|
writer->PrintValue(type);
|
|
if (name != kInvalidString) {
|
|
writer->PrintValue(name);
|
|
} else {
|
|
ASSERT(profile_writer != nullptr);
|
|
// If we don't already have a name for the node, we lazily create a default
|
|
// one. This is safe since the strings table is written out after the nodes.
|
|
const intptr_t name = profile_writer->strings_.AddFormatted(
|
|
"Unnamed [%s] (nil)", profile_writer->node_types_.At(type));
|
|
writer->PrintValue(name);
|
|
}
|
|
id.Write(writer);
|
|
writer->PrintValue(self_size);
|
|
writer->PrintValue64(edges->Length());
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::NodeInfo::WriteDebug(
|
|
V8SnapshotProfileWriter* profile_writer,
|
|
JSONWriter* writer) const {
|
|
writer->OpenObject();
|
|
if (type != kInvalidString) {
|
|
writer->PrintProperty("type", profile_writer->node_types_.At(type));
|
|
}
|
|
if (name != kInvalidString) {
|
|
writer->PrintProperty("name", profile_writer->strings_.At(name));
|
|
}
|
|
id.WriteDebug(writer, "id");
|
|
writer->PrintProperty("self_size", self_size);
|
|
edges->WriteDebug(profile_writer, writer, "edges");
|
|
writer->CloseObject();
|
|
}
|
|
|
|
const char* V8SnapshotProfileWriter::ObjectId::ToCString(Zone* zone) const {
|
|
JSONWriter writer;
|
|
WriteDebug(&writer);
|
|
return OS::SCreate(zone, "%s", writer.buffer()->buffer());
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::ObjectId::Write(JSONWriter* writer,
|
|
const char* property) const {
|
|
if (property != nullptr) {
|
|
writer->PrintProperty64(property, encoded_);
|
|
} else {
|
|
writer->PrintValue64(encoded_);
|
|
}
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::ObjectId::WriteDebug(JSONWriter* writer,
|
|
const char* property) const {
|
|
writer->OpenObject(property);
|
|
writer->PrintProperty("space", IdSpaceToCString(space()));
|
|
writer->PrintProperty64("nonce", nonce());
|
|
writer->CloseObject();
|
|
}
|
|
|
|
const char* V8SnapshotProfileWriter::ObjectId::IdSpaceToCString(IdSpace space) {
|
|
switch (space) {
|
|
case IdSpace::kInvalid:
|
|
return "Invalid";
|
|
case IdSpace::kSnapshot:
|
|
return "Snapshot";
|
|
case IdSpace::kVmText:
|
|
return "VmText";
|
|
case IdSpace::kIsolateText:
|
|
return "IsolateText";
|
|
case IdSpace::kVmData:
|
|
return "VmData";
|
|
case IdSpace::kIsolateData:
|
|
return "IsolateData";
|
|
case IdSpace::kArtificial:
|
|
return "Artificial";
|
|
default:
|
|
UNREACHABLE();
|
|
}
|
|
}
|
|
|
|
const char* V8SnapshotProfileWriter::EdgeMap::ToCString(
|
|
V8SnapshotProfileWriter* profile_writer,
|
|
Zone* zone) const {
|
|
JSONWriter writer;
|
|
WriteDebug(profile_writer, &writer);
|
|
return OS::SCreate(zone, "%s", writer.buffer()->buffer());
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::EdgeMap::WriteDebug(
|
|
V8SnapshotProfileWriter* profile_writer,
|
|
JSONWriter* writer,
|
|
const char* property) const {
|
|
writer->OpenArray(property);
|
|
auto edge_it = GetIterator();
|
|
while (auto const pair = edge_it.Next()) {
|
|
pair->edge.WriteDebug(profile_writer, writer, pair->target);
|
|
}
|
|
writer->CloseArray();
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::Edge::Write(
|
|
V8SnapshotProfileWriter* profile_writer,
|
|
JSONWriter* writer,
|
|
const ObjectId& target_id) const {
|
|
ASSERT(type != Type::kInvalid);
|
|
writer->PrintValue64(static_cast<intptr_t>(type));
|
|
writer->PrintValue64(name_or_offset);
|
|
auto const target = profile_writer->nodes_.LookupValue(target_id);
|
|
writer->PrintValue64(target.offset());
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::Edge::WriteDebug(
|
|
V8SnapshotProfileWriter* profile_writer,
|
|
JSONWriter* writer,
|
|
const ObjectId& target_id) const {
|
|
writer->OpenObject();
|
|
if (type != Type::kInvalid) {
|
|
writer->PrintProperty(
|
|
"type", profile_writer->edge_types_.At(static_cast<intptr_t>(type)));
|
|
}
|
|
if (type == Type::kProperty) {
|
|
writer->PrintProperty("name", profile_writer->strings_.At(name_or_offset));
|
|
} else {
|
|
writer->PrintProperty64("offset", name_or_offset);
|
|
}
|
|
auto const target = profile_writer->nodes_.LookupValue(target_id);
|
|
target.id.WriteDebug(writer, "target");
|
|
writer->CloseObject();
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::AddRoot(const ObjectId& object_id,
|
|
const char* name) {
|
|
// HeapSnapshotWorker.HeapSnapshot.calculateDistances (from HeapSnapshot.js)
|
|
// assumes that the root does not have more than one edge to any other node
|
|
// (most likely an oversight).
|
|
if (roots_.HasKey(object_id)) return;
|
|
roots_.Insert(object_id);
|
|
|
|
auto const str_index = strings_.Add(name);
|
|
auto const root = nodes_.Lookup(kArtificialRootId);
|
|
ASSERT(root != nullptr);
|
|
root->AddEdge(str_index != kInvalidString
|
|
? Edge(this, Edge::Type::kProperty, str_index)
|
|
: Edge(this, Edge::Type::kInternal, root->edges->Length()),
|
|
object_id);
|
|
}
|
|
|
|
intptr_t V8SnapshotProfileWriter::StringsTable::Add(const char* str) {
|
|
if (str == nullptr) return kInvalidString;
|
|
if (auto const kv = index_map_.Lookup(str)) {
|
|
return kv->value;
|
|
}
|
|
const char* new_str = OS::SCreate(zone_, "%s", str);
|
|
const intptr_t index = strings_.length();
|
|
strings_.Add(new_str);
|
|
index_map_.Insert({new_str, index});
|
|
return index;
|
|
}
|
|
|
|
intptr_t V8SnapshotProfileWriter::StringsTable::AddFormatted(const char* fmt,
|
|
...) {
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
const char* str = OS::VSCreate(zone_, fmt, args);
|
|
va_end(args);
|
|
if (auto const kv = index_map_.Lookup(str)) {
|
|
return kv->value;
|
|
}
|
|
const intptr_t index = strings_.length();
|
|
strings_.Add(str);
|
|
index_map_.Insert({str, index});
|
|
return index;
|
|
}
|
|
|
|
const char* V8SnapshotProfileWriter::StringsTable::At(intptr_t index) const {
|
|
if (index > strings_.length()) return nullptr;
|
|
return strings_[index];
|
|
}
|
|
|
|
void V8SnapshotProfileWriter::StringsTable::Write(JSONWriter* writer,
|
|
const char* property) const {
|
|
writer->OpenArray(property);
|
|
for (auto const str : strings_) {
|
|
writer->PrintValue(str);
|
|
writer->PrintNewline();
|
|
}
|
|
writer->CloseArray();
|
|
}
|
|
|
|
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");
|
|
node_types_.Write(writer);
|
|
writer->CloseArray();
|
|
}
|
|
|
|
{
|
|
writer->OpenArray("edge_fields");
|
|
writer->PrintValue("type");
|
|
writer->PrintValue("name_or_index");
|
|
writer->PrintValue("to_node");
|
|
writer->CloseArray();
|
|
}
|
|
|
|
{
|
|
writer->OpenArray("edge_types");
|
|
edge_types_.Write(writer);
|
|
writer->CloseArray();
|
|
}
|
|
|
|
writer->CloseObject();
|
|
|
|
writer->PrintProperty64("node_count", nodes_.Size());
|
|
{
|
|
intptr_t edge_count = 0;
|
|
auto nodes_it = nodes_.GetIterator();
|
|
while (auto const info = nodes_it.Next()) {
|
|
// All nodes should have an edge map, though it may be empty.
|
|
ASSERT(info->edges != nullptr);
|
|
edge_count += info->edges->Length();
|
|
}
|
|
writer->PrintProperty64("edge_count", edge_count);
|
|
}
|
|
}
|
|
writer->CloseObject();
|
|
|
|
{
|
|
writer->OpenArray("nodes");
|
|
// Always write the information for the artificial root first.
|
|
auto const root = nodes_.Lookup(kArtificialRootId);
|
|
ASSERT(root != nullptr);
|
|
intptr_t offset = 0;
|
|
root->set_offset(offset);
|
|
root->Write(this, writer);
|
|
offset += kNumNodeFields;
|
|
auto nodes_it = nodes_.GetIterator();
|
|
for (auto entry = nodes_it.Next(); entry != nullptr;
|
|
entry = nodes_it.Next()) {
|
|
if (entry->id == kArtificialRootId) continue;
|
|
entry->set_offset(offset);
|
|
entry->Write(this, writer);
|
|
offset += kNumNodeFields;
|
|
}
|
|
writer->CloseArray();
|
|
}
|
|
|
|
{
|
|
auto write_edges = [&](const NodeInfo& info) {
|
|
auto edges_it = info.edges->GetIterator();
|
|
while (auto const pair = edges_it.Next()) {
|
|
pair->edge.Write(this, writer, pair->target);
|
|
}
|
|
};
|
|
writer->OpenArray("edges");
|
|
// Always write the information for the artificial root first.
|
|
auto const root = nodes_.Lookup(kArtificialRootId);
|
|
ASSERT(root != nullptr);
|
|
write_edges(*root);
|
|
auto nodes_it = nodes_.GetIterator();
|
|
while (auto const entry = nodes_it.Next()) {
|
|
if (entry->id == kArtificialRootId) continue;
|
|
write_edges(*entry);
|
|
}
|
|
writer->CloseArray();
|
|
}
|
|
|
|
// Must happen after any calls to WriteNodeInfo, as those calls may add more
|
|
// strings.
|
|
strings_.Write(writer, "strings");
|
|
|
|
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("warning: Could not access file callbacks.");
|
|
return;
|
|
}
|
|
|
|
auto file = file_open(filename, /*write=*/true);
|
|
if (file == nullptr) {
|
|
OS::PrintErr("warning: Failed to write snapshot profile: %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);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
} // namespace dart
|