[vm/aot] Fill in names of properties in snapshot profile.
Change-Id: Ic709a3dc40390463ffb5fd76fd8eb63ebd87acc0
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-precomp-mac-release-simarm64-try
Reviewed-on: https://dart-review.googlesource.com/c/85292
Commit-Queue: Samir Jindel <sjindel@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
diff --git a/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart b/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart
index ede572c..000ddcc 100644
--- a/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart
+++ b/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart
@@ -12,20 +12,13 @@
return "/" + segments.join("/");
}
-test(bool use_elf) async {
+test(String sdkRoot, {bool useElf: false}) async {
if (Platform.isWindows) return;
- if (Platform.isMacOS && use_elf) return;
-
- final String outputDir = Platform.isMacOS ? "xcodebuild" : "out";
-
- final List<String> sdkBaseSegments =
- Uri.file(Platform.resolvedExecutable).pathSegments.toList();
- sdkBaseSegments.replaceRange(
- sdkBaseSegments.indexOf(outputDir), sdkBaseSegments.length, []);
+ if (Platform.isMacOS && useElf) return;
// Generate the snapshot profile.
- final String thisTestPath = path(sdkBaseSegments) +
- "/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart";
+ final String thisTestPath =
+ "$sdkRoot/runtime/tests/vm/dart/v8_snapshot_profile_writer_test.dart";
final Directory temp = await Directory.systemTemp.createTemp();
final String snapshotPath = temp.path + "/test.snap";
@@ -36,20 +29,20 @@
snapshotPath,
];
- if (use_elf) {
+ if (useElf) {
precompiler2Args.insert(0, "--build-elf");
}
final ProcessResult result = await Process.run(
"pkg/vm/tool/precompiler2",
precompiler2Args,
- workingDirectory: path(sdkBaseSegments),
+ workingDirectory: sdkRoot,
runInShell: true,
);
// The precompiler2 script tried using GCC for the wrong architecture. We
// don't have a workaround for this now.
- if (use_elf &&
+ if (useElf &&
result.exitCode != 0 &&
result.stderr.contains("Assembler messages")) {
return;
@@ -91,7 +84,7 @@
// 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) {
+ if (useElf) {
await Process.run("strip", [snapshotPath]);
}
final int actual = await File(snapshotPath).length();
@@ -99,7 +92,85 @@
Expect.isTrue((actual - expected).abs() / actual < 0.01);
}
+Match matchComplete(RegExp regexp, String line) {
+ Match match = regexp.firstMatch(line);
+ if (match == null) return match;
+ if (match.start != 0 || match.end != line.length) return null;
+ return match;
+}
+
+// All fields of "Raw..." classes defined in "raw_object.h" must be included in
+// the giant macro in "raw_object_fields.cc". This function attempts to check
+// that with some basic regexes.
+testMacros(String sdkRoot) async {
+ const String className = "([a-z0-9A-Z]+)";
+ const String rawClass = "Raw$className";
+ const String fieldName = "([a-z0-9A-Z_]+)";
+
+ final Map<String, Set<String>> fields = {};
+
+ final String rawObjectFieldsPath = "$sdkRoot/runtime/vm/raw_object_fields.cc";
+ final RegExp fieldEntry = RegExp(" *F\\($className, $fieldName\\) *\\\\?");
+
+ await for (String line in File(rawObjectFieldsPath)
+ .openRead()
+ .transform(utf8.decoder)
+ .transform(LineSplitter())) {
+ Match match = matchComplete(fieldEntry, line);
+ if (match != null) {
+ fields
+ .putIfAbsent(match.group(1), () => Set<String>())
+ .add(match.group(2));
+ }
+ }
+
+ final RegExp classStart = RegExp("class $rawClass : public $rawClass {");
+ final RegExp classEnd = RegExp("}");
+ final RegExp field = RegExp(" $rawClass. +$fieldName;.*");
+
+ final String rawObjectPath = "$sdkRoot/runtime/vm/raw_object.h";
+
+ String currentClass;
+ bool hasMissingFields = false;
+ await for (String line in File(rawObjectPath)
+ .openRead()
+ .transform(utf8.decoder)
+ .transform(LineSplitter())) {
+ Match match = matchComplete(classStart, line);
+ if (match != null) {
+ currentClass = match.group(1);
+ continue;
+ }
+
+ match = matchComplete(classEnd, line);
+ if (match != null) {
+ currentClass = null;
+ continue;
+ }
+
+ match = matchComplete(field, line);
+ if (match != null && currentClass != null) {
+ if (!fields[currentClass].contains(match.group(2))) {
+ hasMissingFields = true;
+ print("$currentClass is missing ${match.group(2)}.");
+ }
+ }
+ }
+
+ if (hasMissingFields) {
+ Expect.fail(
+ "runtime/vm/raw_object_fields.cc misses some fields. Please update it to match raw_object.h.");
+ }
+}
+
main() async {
- test(false);
- test(true);
+ final List<String> sdkBaseSegments =
+ Uri.file(Platform.resolvedExecutable).pathSegments.toList();
+ sdkBaseSegments
+ .replaceRange(sdkBaseSegments.length - 3, sdkBaseSegments.length, []);
+ String sdkRoot = path(sdkBaseSegments);
+
+ test(sdkRoot, useElf: false);
+ test(sdkRoot, useElf: true);
+ testMacros(sdkRoot);
}
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index acbd08a..118143ed 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -299,9 +299,9 @@
s->Write<bool>(type_args->IsCanonical());
intptr_t hash = Smi::Value(type_args->ptr()->hash_);
s->Write<int32_t>(hash);
- s->WriteRef(type_args->ptr()->instantiations_);
+ WriteField(type_args, instantiations_);
for (intptr_t j = 0; j < length; j++) {
- s->WriteRef(type_args->ptr()->types()[j]);
+ s->WriteElementRef(type_args->ptr()->types()[j], j);
}
}
}
@@ -641,11 +641,11 @@
RawClosureData* data = objects_[i];
AutoTraceObject(data);
if (s->kind() != Snapshot::kFullAOT) {
- s->WriteRef(data->ptr()->context_scope_);
+ WriteField(data, context_scope_);
}
- s->WriteRef(data->ptr()->parent_function_);
- s->WriteRef(data->ptr()->signature_type_);
- s->WriteRef(data->ptr()->closure_);
+ WriteField(data, parent_function_);
+ WriteField(data, signature_type_);
+ WriteField(data, closure_);
}
}
@@ -882,39 +882,39 @@
intptr_t count = objects_.length();
for (intptr_t i = 0; i < count; i++) {
RawField* field = objects_[i];
- AutoTraceObject(field);
+ AutoTraceObjectName(field, field->ptr()->name_);
- s->WriteRef(field->ptr()->name_);
- s->WriteRef(field->ptr()->owner_);
- s->WriteRef(field->ptr()->type_);
+ WriteField(field, name_);
+ WriteField(field, owner_);
+ WriteField(field, type_);
// Write out the initial static value or field offset.
if (Field::StaticBit::decode(field->ptr()->kind_bits_)) {
if (kind == Snapshot::kFullAOT) {
// For precompiled static fields, the value was already reset and
// initializer_ now contains a Function.
- s->WriteRef(field->ptr()->value_.static_value_);
+ WriteField(field, value_.static_value_);
} else if (Field::ConstBit::decode(field->ptr()->kind_bits_)) {
// Do not reset const fields.
- s->WriteRef(field->ptr()->value_.static_value_);
+ WriteField(field, value_.static_value_);
} else {
// Otherwise, for static fields we write out the initial static value.
- s->WriteRef(field->ptr()->initializer_.saved_value_);
+ WriteField(field, initializer_.saved_value_);
}
} else {
- s->WriteRef(field->ptr()->value_.offset_);
+ WriteField(field, value_.offset_);
}
// Write out the initializer function or saved initial value.
if (kind == Snapshot::kFullAOT) {
- s->WriteRef(field->ptr()->initializer_.precompiled_);
+ WriteField(field, initializer_.precompiled_);
} else {
- s->WriteRef(field->ptr()->initializer_.saved_value_);
+ WriteField(field, initializer_.saved_value_);
}
if (kind != Snapshot::kFullAOT) {
// Write out the guarded list length.
- s->WriteRef(field->ptr()->guarded_list_length_);
+ WriteField(field, guarded_list_length_);
}
if (kind == Snapshot::kFullJIT) {
- s->WriteRef(field->ptr()->dependent_code_);
+ WriteField(field, dependent_code_);
}
if (kind != Snapshot::kFullAOT) {
@@ -1369,29 +1369,29 @@
s->WriteInstructions(code->ptr()->active_instructions_, code);
}
- s->WriteRef(code->ptr()->object_pool_);
- s->WriteRef(code->ptr()->owner_);
- s->WriteRef(code->ptr()->exception_handlers_);
- s->WriteRef(code->ptr()->pc_descriptors_);
+ WriteField(code, object_pool_);
+ WriteField(code, owner_);
+ WriteField(code, exception_handlers_);
+ WriteField(code, pc_descriptors_);
#if defined(DART_PRECOMPILED_RUNTIME) || defined(DART_PRECOMPILER)
- s->WriteRef(code->ptr()->catch_entry_.catch_entry_moves_maps_);
+ WriteField(code, catch_entry_.catch_entry_moves_maps_);
#else
- s->WriteRef(code->ptr()->catch_entry_.variables_);
+ WriteField(code, catch_entry_.variables_);
#endif
- s->WriteRef(code->ptr()->stackmaps_);
+ WriteField(code, stackmaps_);
if (FLAG_dwarf_stack_traces) {
- s->WriteRef(Array::null());
- s->WriteRef(CodeSourceMap::null());
+ WriteFieldValue(inlined_id_to_function_, Array::null());
+ WriteFieldValue(code_source_map_, CodeSourceMap::null());
} else {
- s->WriteRef(code->ptr()->inlined_id_to_function_);
- s->WriteRef(code->ptr()->code_source_map_);
+ WriteField(code, inlined_id_to_function_);
+ WriteField(code, code_source_map_);
}
if (kind == Snapshot::kFullJIT) {
- s->WriteRef(code->ptr()->deopt_info_array_);
- s->WriteRef(code->ptr()->static_calls_target_table_);
+ WriteField(code, deopt_info_array_);
+ WriteField(code, static_calls_target_table_);
}
- NOT_IN_PRODUCT(s->WriteRef(code->ptr()->await_token_positions_));
- NOT_IN_PRODUCT(s->WriteRef(code->ptr()->return_address_metadata_));
+ NOT_IN_PRODUCT(WriteField(code, await_token_positions_));
+ NOT_IN_PRODUCT(WriteField(code, return_address_metadata_));
s->Write<int32_t>(code->ptr()->state_bits_);
}
@@ -1607,11 +1607,11 @@
// Natives can run while precompiling, becoming linked and
// switching their stub. Reset to the initial stub used for
// lazy-linking.
- s->WriteRef(StubCode::CallBootstrapNative().raw());
+ s->WriteElementRef(StubCode::CallBootstrapNative().raw(), j);
break;
}
#endif
- s->WriteRef(entry.raw_obj_);
+ s->WriteElementRef(entry.raw_obj_, j);
break;
}
case ObjectPool::kImmediate: {
@@ -1631,7 +1631,7 @@
payload->trampoline = NULL;
payload->native_function = NULL;
}
- s->WriteRef(raw);
+ s->WriteElementRef(raw, j);
break;
}
case ObjectPool::kNativeFunction:
@@ -1758,13 +1758,18 @@
for (intptr_t i = 0; i < count; i++) {
RawObject* object = objects_[i];
s->AssignRef(object);
- AutoTraceObject(object);
+ if (cid_ == kOneByteStringCid || cid_ == kTwoByteStringCid) {
+ s->TraceStartWritingObject(name(), object, String::RawCast(object));
+ } else {
+ s->TraceStartWritingObject(name(), object, nullptr);
+ }
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->TraceEndWritingObject();
}
}
@@ -1838,7 +1843,7 @@
AutoTraceObject(handlers);
intptr_t length = handlers->ptr()->num_entries_;
s->WriteUnsigned(length);
- s->WriteRef(handlers->ptr()->handled_types_data_);
+ WriteField(handlers, handled_types_data_);
for (intptr_t j = 0; j < length; j++) {
const ExceptionHandlerInfo& info = handlers->ptr()->data()[j];
s->Write<uint32_t>(info.handler_pc_offset);
@@ -1934,9 +1939,9 @@
AutoTraceObject(context);
intptr_t length = context->ptr()->num_variables_;
s->WriteUnsigned(length);
- s->WriteRef(context->ptr()->parent_);
+ WriteField(context, parent_);
for (intptr_t j = 0; j < length; j++) {
- s->WriteRef(context->ptr()->data()[j]);
+ s->WriteElementRef(context->ptr()->data()[j], j);
}
}
}
@@ -2296,7 +2301,7 @@
for (intptr_t i = 0; i < count; i++) {
RawSubtypeTestCache* cache = objects_[i];
AutoTraceObject(cache);
- s->WriteRef(cache->ptr()->cache_);
+ WriteField(cache, cache_);
}
}
@@ -2525,7 +2530,7 @@
while (offset < next_field_offset) {
RawObject* raw_obj = *reinterpret_cast<RawObject**>(
reinterpret_cast<uword>(instance->ptr()) + offset);
- s->WriteRef(raw_obj);
+ s->WriteElementRef(raw_obj, offset);
offset += kWordSize;
}
}
@@ -3810,7 +3815,7 @@
AutoTraceObject(map);
s->Write<bool>(map->IsCanonical());
- s->WriteRef(map->ptr()->type_arguments_);
+ WriteField(map, type_arguments_);
const intptr_t used_data = Smi::Value(map->ptr()->used_data_);
ASSERT((used_data & 1) == 0); // Keys + values, so must be even.
@@ -3825,8 +3830,8 @@
RawObject* key = data_elements[i];
if (key != data_array) {
RawObject* value = data_elements[i + 1];
- s->WriteRef(key);
- s->WriteRef(value);
+ s->WriteElementRef(key, i);
+ s->WriteElementRef(value, i + 1);
}
}
}
@@ -3934,9 +3939,9 @@
intptr_t length = Smi::Value(array->ptr()->length_);
s->WriteUnsigned(length);
s->Write<bool>(array->IsCanonical());
- s->WriteRef(array->ptr()->type_arguments_);
+ WriteField(array, type_arguments_);
for (intptr_t j = 0; j < length; j++) {
- s->WriteRef(array->ptr()->data()[j]);
+ s->WriteElementRef(array->ptr()->data()[j], j);
}
}
}
@@ -4191,6 +4196,9 @@
for (intptr_t i = 0; i < num_cids_; i++) {
clusters_by_cid_[i] = NULL;
}
+ if (profile_writer_ != nullptr) {
+ offsets_table_ = new (zone_) OffsetsTable(zone_);
+ }
}
Serializer::~Serializer() {
@@ -4202,11 +4210,14 @@
RawString* name) {
if (profile_writer_ == nullptr) return;
+ intptr_t cid = -1;
intptr_t id = 0;
if (obj->IsHeapObject()) {
id = heap_->GetObjectId(obj);
+ cid = obj->GetClassId();
} else {
id = smi_ids_.Lookup(Smi::RawCast(obj))->id_;
+ cid = Smi::kClassId;
}
ASSERT(id != 0);
@@ -4217,20 +4228,21 @@
name_str = str.ToCString();
}
- object_currently_writing_id_ = id;
+ object_currently_writing_.object_ = obj;
+ object_currently_writing_.id_ = id;
+ object_currently_writing_.stream_start_ = stream_.Position();
+ object_currently_writing_.cid_ = cid;
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);
+ 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;
+ {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_},
+ stream_.Position() - object_currently_writing_.stream_start_);
+ object_currently_writing_ = ProfilingObject();
}
}
@@ -4377,24 +4389,33 @@
// 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);
+ ASSERT(object_currently_writing_.id_ != 0);
auto offset_space = vm_ ? V8SnapshotProfileWriter::kVmText
: V8SnapshotProfileWriter::kIsolateText;
+ V8SnapshotProfileWriter::ObjectId to_object = {
+ offset_space, offset < 0 ? -offset : offset};
+ V8SnapshotProfileWriter::ObjectId from_object = {
+ V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_};
profile_writer_->AttributeReferenceTo(
- {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_id_},
- {offset_space, offset < 0 ? -offset : offset});
+ from_object, {to_object, V8SnapshotProfileWriter::Reference::kProperty,
+ profile_writer_->EnsureString("<instructions>")});
}
}
void Serializer::TraceDataOffset(uint32_t offset) {
if (profile_writer_ != nullptr) {
// ROData cannot be roots.
- ASSERT(object_currently_writing_id_ != 0);
+ ASSERT(object_currently_writing_.id_ != 0);
auto offset_space = vm_ ? V8SnapshotProfileWriter::kVmData
: V8SnapshotProfileWriter::kIsolateData;
+ V8SnapshotProfileWriter::ObjectId from_object = {
+ V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_};
+ V8SnapshotProfileWriter::ObjectId to_object = {offset_space, offset};
+ // TODO(sjindel): Give this edge a more appropriate type than element
+ // (internal, maybe?).
profile_writer_->AttributeReferenceTo(
- {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_id_},
- {offset_space, offset});
+ from_object,
+ {to_object, V8SnapshotProfileWriter::Reference::kElement, 0});
}
}
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index 79bdd8d..412ffb5 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -14,6 +14,7 @@
#include "vm/hash_map.h"
#include "vm/heap/heap.h"
#include "vm/object.h"
+#include "vm/raw_object_fields.h"
#include "vm/snapshot.h"
#include "vm/type_testing_stubs.h"
#include "vm/v8_snapshot_writer.h"
@@ -236,7 +237,8 @@
}
void Align(intptr_t alignment) { stream_.Align(alignment); }
- void WriteRef(RawObject* object, bool is_root = false) {
+ private:
+ intptr_t WriteRefId(RawObject* object) {
intptr_t id = 0;
if (!object->IsHeapObject()) {
RawSmi* smi = Smi::RawCast(object);
@@ -251,35 +253,75 @@
id = heap_->GetObjectId(object);
if (id == 0) {
if (object->IsCode() && !Snapshot::IncludesCode(kind_)) {
- WriteRef(Object::null());
- return;
+ return WriteRefId(Object::null());
}
#if !defined(DART_PRECOMPILED_RUNTIME)
if (object->IsBytecode() && !Snapshot::IncludesBytecode(kind_)) {
- WriteRef(Object::null());
- return;
+ return WriteRefId(Object::null());
}
#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;
+ return WriteRefId(Object::null());
}
FATAL("Missing ref");
}
}
+ return id;
+ }
+ public:
+ void WriteRootRef(RawObject* object) {
+ intptr_t id = WriteRefId(object);
WriteUnsigned(id);
-
if (profile_writer_ != nullptr) {
- if (object_currently_writing_id_ != 0) {
+ profile_writer_->AddRoot({V8SnapshotProfileWriter::kSnapshot, id});
+ }
+ }
+
+ void WriteElementRef(RawObject* object, intptr_t index) {
+ intptr_t id = WriteRefId(object);
+ WriteUnsigned(id);
+ if (profile_writer_ != nullptr) {
+ profile_writer_->AttributeReferenceTo(
+ {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_},
+ {{V8SnapshotProfileWriter::kSnapshot, id},
+ V8SnapshotProfileWriter::Reference::kElement,
+ index});
+ }
+ }
+
+ void WritePropertyRef(RawObject* object, const char* property) {
+ intptr_t id = WriteRefId(object);
+ WriteUnsigned(id);
+ if (profile_writer_ != nullptr) {
+ profile_writer_->AttributeReferenceTo(
+ {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_},
+ {{V8SnapshotProfileWriter::kSnapshot, id},
+ V8SnapshotProfileWriter::Reference::kProperty,
+ profile_writer_->EnsureString(property)});
+ }
+ }
+
+ void WriteOffsetRef(RawObject* object, intptr_t offset) {
+ intptr_t id = WriteRefId(object);
+ WriteUnsigned(id);
+ if (profile_writer_ != nullptr) {
+ const char* property = offsets_table_->FieldNameForOffset(
+ object_currently_writing_.cid_, offset);
+ if (property != nullptr) {
profile_writer_->AttributeReferenceTo(
- {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_id_},
- {V8SnapshotProfileWriter::kSnapshot, id});
+ {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_},
+ {{V8SnapshotProfileWriter::kSnapshot, id},
+ V8SnapshotProfileWriter::Reference::kProperty,
+ profile_writer_->EnsureString(property)});
} else {
- ASSERT(is_root);
- profile_writer_->AddRoot({V8SnapshotProfileWriter::kSnapshot, id});
+ profile_writer_->AttributeReferenceTo(
+ {V8SnapshotProfileWriter::kSnapshot, object_currently_writing_.id_},
+ {{V8SnapshotProfileWriter::kSnapshot, id},
+ V8SnapshotProfileWriter::Reference::kElement,
+ offset});
}
}
}
@@ -289,7 +331,8 @@
RawObject** from = obj->from();
RawObject** to = obj->to_snapshot(kind(), args...);
for (RawObject** p = from; p <= to; p++) {
- WriteRef(*p);
+ WriteOffsetRef(*p, (p - reinterpret_cast<RawObject**>(obj->ptr())) *
+ sizeof(RawObject*));
}
}
@@ -302,10 +345,6 @@
}
}
- void WriteRootRef(RawObject* object) {
- WriteRef(object, /* is_root = */ true);
- }
-
void WriteTokenPosition(TokenPosition pos) {
Write<int32_t>(pos.SnapshotEncode());
}
@@ -346,8 +385,13 @@
bool vm_;
V8SnapshotProfileWriter* profile_writer_ = nullptr;
- intptr_t object_currently_writing_id_ = 0;
- intptr_t object_currently_writing_start_ = 0;
+ struct ProfilingObject {
+ RawObject* object_ = nullptr;
+ intptr_t id_ = 0;
+ intptr_t stream_start_ = 0;
+ intptr_t cid_ = -1;
+ } object_currently_writing_;
+ OffsetsTable* offsets_table_ = nullptr;
#if defined(SNAPSHOT_BACKTRACE)
RawObject* current_parent_;
@@ -363,14 +407,14 @@
#define AutoTraceObjectName(obj, str) \
SerializerWritingObjectScope scope_##__COUNTER__(s, name(), obj, str)
-#define WriteField(obj, field) s->WriteRef(obj->ptr()->field);
-
-#define WriteFieldValue(field, value) s->WriteRef(value);
+#define WriteFieldValue(field, value) s->WritePropertyRef(value, #field);
#define WriteFromTo(obj, ...) s->WriteFromTo(obj, ##__VA_ARGS__);
#define PushFromTo(obj, ...) s->PushFromTo(obj, ##__VA_ARGS__);
+#define WriteField(obj, field) s->WritePropertyRef(obj->ptr()->field, #field)
+
struct SerializerWritingObjectScope {
SerializerWritingObjectScope(Serializer* serializer,
const char* type,
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index f61c467..cd8ef90 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -294,7 +294,7 @@
for (intptr_t i = 0; i < objects_.length(); i++) {
const Object& obj = *objects_[i].obj_;
- AutoTraceImage(section_start, stream, "ROData");
+ AutoTraceImage(obj, section_start, stream);
NoSafepointScope no_safepoint;
uword start = reinterpret_cast<uword>(obj.raw()) - kHeapObjectTag;
@@ -615,7 +615,7 @@
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");
+ AutoTraceImage(insns, 0, &this->instructions_blob_stream_);
uword beginning = reinterpret_cast<uword>(insns.raw_ptr());
uword entry = beginning + Instructions::HeaderSize();
diff --git a/runtime/vm/image_snapshot.h b/runtime/vm/image_snapshot.h
index 33c0956..a56c87e 100644
--- a/runtime/vm/image_snapshot.h
+++ b/runtime/vm/image_snapshot.h
@@ -13,6 +13,8 @@
#include "vm/globals.h"
#include "vm/growable_array.h"
#include "vm/hash_map.h"
+#include "vm/object.h"
+#include "vm/reusable_handles.h"
#include "vm/v8_snapshot_writer.h"
namespace dart {
@@ -184,10 +186,10 @@
DISALLOW_COPY_AND_ASSIGN(ImageWriter);
};
-#define AutoTraceImage(section_offset, stream, type_name) \
+#define AutoTraceImage(object, section_offset, stream) \
auto AutoTraceImagObjectScopeVar##__COUNTER__ = \
TraceImageObjectScope<std::remove_pointer<decltype(stream)>::type>( \
- this, section_offset, stream, type_name);
+ this, section_offset, stream, object);
template <typename T>
class TraceImageObjectScope {
@@ -195,15 +197,22 @@
TraceImageObjectScope(ImageWriter* writer,
intptr_t section_offset,
const T* stream,
- const char* type)
+ const Object& object)
: writer_(writer),
stream_(stream),
section_offset_(section_offset),
start_offset_(stream_->Position() - section_offset) {
if (writer_->profile_writer_ != nullptr) {
+ Thread* thread = Thread::Current();
+ REUSABLE_CLASS_HANDLESCOPE(thread);
+ REUSABLE_STRING_HANDLESCOPE(thread);
+ Class& klass = thread->ClassHandle();
+ String& name = thread->StringHandle();
+ klass = object.clazz();
+ name = klass.UserVisibleName();
ASSERT(writer_->offset_space_ != V8SnapshotProfileWriter::kSnapshot);
writer_->profile_writer_->SetObjectTypeAndName(
- {writer_->offset_space_, start_offset_}, type, nullptr);
+ {writer_->offset_space_, start_offset_}, name.ToCString(), nullptr);
}
}
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 3f9ef48..01f4cae 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -267,6 +267,7 @@
friend class InterpreterHelpers; \
friend class Simulator; \
friend class SimulatorHelpers; \
+ friend class OffsetsTable; \
DISALLOW_ALLOCATION(); \
DISALLOW_IMPLICIT_CONSTRUCTORS(Raw##object)
@@ -878,6 +879,7 @@
friend class Precompiler; // GetClassId
friend class ObjectOffsetTrait; // GetClassId
friend class WriteBarrierUpdateVisitor; // CheckHeapPointerStore
+ friend class OffsetsTable;
DISALLOW_ALLOCATION();
DISALLOW_IMPLICIT_CONSTRUCTORS(RawObject);
diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc
new file mode 100644
index 0000000..9e1f8e5
--- /dev/null
+++ b/runtime/vm/raw_object_fields.cc
@@ -0,0 +1,217 @@
+// 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/raw_object_fields.h"
+
+namespace dart {
+
+#if defined(DART_PRECOMPILER)
+
+#define RAW_CLASSES_AND_FIELDS(F) \
+ F(Class, name_) \
+ F(Class, user_name_) \
+ F(Class, functions_) \
+ F(Class, functions_hash_table_) \
+ F(Class, fields_) \
+ F(Class, offset_in_words_to_field_) \
+ F(Class, interfaces_) \
+ F(Class, script_) \
+ F(Class, library_) \
+ F(Class, type_parameters_) \
+ F(Class, super_type_) \
+ F(Class, mixin_) \
+ F(Class, signature_function_) \
+ F(Class, constants_) \
+ F(Class, canonical_type_) \
+ F(Class, invocation_dispatcher_cache_) \
+ F(Class, allocation_stub_) \
+ F(Class, direct_implementors_) \
+ F(Class, direct_subclasses_) \
+ F(Class, dependent_code_) \
+ F(PatchClass, patched_class_) \
+ F(PatchClass, origin_class_) \
+ F(PatchClass, script_) \
+ F(PatchClass, library_kernel_data_) \
+ F(Function, name_) \
+ F(Function, owner_) \
+ F(Function, result_type_) \
+ F(Function, parameter_types_) \
+ F(Function, parameter_names_) \
+ F(Function, type_parameters_) \
+ F(Function, data_) \
+ F(Function, ic_data_array_) \
+ F(Function, code_) \
+ F(ClosureData, context_scope_) \
+ F(ClosureData, parent_function_) \
+ F(ClosureData, signature_type_) \
+ F(ClosureData, closure_) \
+ F(SignatureData, parent_function_) \
+ F(SignatureData, signature_type_) \
+ F(RedirectionData, type_) \
+ F(RedirectionData, identifier_) \
+ F(RedirectionData, target_) \
+ F(Field, name_) \
+ F(Field, owner_) \
+ F(Field, type_) \
+ F(Field, guarded_list_length_) \
+ F(Field, dependent_code_) \
+ F(Field, type_test_cache_) \
+ F(Script, url_) \
+ F(Script, resolved_url_) \
+ F(Script, compile_time_constants_) \
+ F(Script, line_starts_) \
+ F(Script, debug_positions_) \
+ F(Script, yield_positions_) \
+ F(Script, kernel_program_info_) \
+ F(Script, source_) \
+ F(Library, name_) \
+ F(Library, url_) \
+ F(Library, private_key_) \
+ F(Library, dictionary_) \
+ F(Library, metadata_) \
+ F(Library, toplevel_class_) \
+ F(Library, patch_classes_) \
+ F(Library, imports_) \
+ F(Library, exports_) \
+ F(Library, load_error_) \
+ F(Library, kernel_data_) \
+ F(Library, resolved_names_) \
+ F(Library, exported_names_) \
+ F(Library, loaded_scripts_) \
+ F(Namespace, library_) \
+ F(Namespace, show_names_) \
+ F(Namespace, hide_names_) \
+ F(Namespace, metadata_field_) \
+ F(KernelProgramInfo, string_offsets_) \
+ F(KernelProgramInfo, string_data_) \
+ F(KernelProgramInfo, canonical_names_) \
+ F(KernelProgramInfo, metadata_payloads_) \
+ F(KernelProgramInfo, metadata_mappings_) \
+ F(KernelProgramInfo, scripts_) \
+ F(KernelProgramInfo, constants_) \
+ F(KernelProgramInfo, potential_natives_) \
+ F(KernelProgramInfo, potential_pragma_functions_) \
+ F(KernelProgramInfo, constants_table_) \
+ F(KernelProgramInfo, libraries_cache_) \
+ F(KernelProgramInfo, classes_cache_) \
+ F(Code, object_pool_) \
+ F(Code, instructions_) \
+ F(Code, owner_) \
+ F(Code, exception_handlers_) \
+ F(Code, pc_descriptors_) \
+ F(Code, stackmaps_) \
+ F(Code, inlined_id_to_function_) \
+ F(Code, code_source_map_) \
+ F(Bytecode, object_pool_) \
+ F(Bytecode, instructions_) \
+ F(Bytecode, function_) \
+ F(Bytecode, exception_handlers_) \
+ F(Bytecode, pc_descriptors_) \
+ F(ExceptionHandlers, handled_types_data_) \
+ F(Context, parent_) \
+ F(SingleTargetCache, target_) \
+ F(UnlinkedCall, target_name_) \
+ F(UnlinkedCall, args_descriptor_) \
+ F(ICData, ic_data_) \
+ F(ICData, target_name_) \
+ F(ICData, args_descriptor_) \
+ F(ICData, owner_) \
+ F(MegamorphicCache, buckets_) \
+ F(MegamorphicCache, mask_) \
+ F(MegamorphicCache, target_name_) \
+ F(MegamorphicCache, args_descriptor_) \
+ F(SubtypeTestCache, cache_) \
+ F(ApiError, message_) \
+ F(LanguageError, previous_error_) \
+ F(LanguageError, script_) \
+ F(LanguageError, message_) \
+ F(LanguageError, formatted_message_) \
+ F(UnhandledException, exception_) \
+ F(UnhandledException, stacktrace_) \
+ F(UnwindError, message_) \
+ F(LibraryPrefix, name_) \
+ F(LibraryPrefix, importer_) \
+ F(LibraryPrefix, imports_) \
+ F(LibraryPrefix, dependent_code_) \
+ F(TypeArguments, instantiations_) \
+ F(TypeArguments, length_) \
+ F(TypeArguments, hash_) \
+ F(Type, type_class_id_) \
+ F(Type, arguments_) \
+ F(Type, hash_) \
+ F(TypeRef, type_) \
+ F(TypeParameter, name_) \
+ F(TypeParameter, hash_) \
+ F(TypeParameter, bound_) \
+ F(TypeParameter, parameterized_function_) \
+ F(BoundedType, type_) \
+ F(BoundedType, bound_) \
+ F(BoundedType, hash_) \
+ F(BoundedType, type_parameter_) \
+ F(MixinAppType, super_type_) \
+ F(MixinAppType, mixin_types_) \
+ F(Closure, instantiator_type_arguments_) \
+ F(Closure, function_type_arguments_) \
+ F(Closure, delayed_type_arguments_) \
+ F(Closure, function_) \
+ F(Closure, context_) \
+ F(Closure, hash_) \
+ F(String, length_) \
+ F(String, hash_) \
+ F(Array, type_arguments_) \
+ F(Array, length_) \
+ F(GrowableObjectArray, type_arguments_) \
+ F(GrowableObjectArray, length_) \
+ F(GrowableObjectArray, data_) \
+ F(LinkedHashMap, type_arguments_) \
+ F(LinkedHashMap, index_) \
+ F(LinkedHashMap, hash_mask_) \
+ F(LinkedHashMap, data_) \
+ F(LinkedHashMap, used_data_) \
+ F(LinkedHashMap, deleted_keys_) \
+ F(TypedData, length_) \
+ F(ExternalTypedData, length_) \
+ F(ReceivePort, send_port_) \
+ F(ReceivePort, handler_) \
+ F(StackTrace, async_link_) \
+ F(StackTrace, code_array_) \
+ F(StackTrace, pc_offset_array_) \
+ F(RegExp, num_bracket_expressions_) \
+ F(RegExp, pattern_) \
+ F(RegExp, external_one_byte_function_) \
+ F(RegExp, external_two_byte_function_) \
+ F(RegExp, external_one_byte_sticky_function_) \
+ F(RegExp, external_two_byte_sticky_function_) \
+ F(WeakProperty, key_) \
+ F(WeakProperty, value_) \
+ F(MirrorReference, referent_) \
+ F(UserTag, label_)
+
+OffsetsTable::OffsetsTable(Zone* zone) : cached_offsets_(zone) {
+ for (intptr_t i = 0; offsets_table[i].class_id != -1; ++i) {
+ OffsetsTableEntry entry = offsets_table[i];
+ cached_offsets_.Insert({{entry.class_id, entry.offset}, entry.field_name});
+ }
+}
+
+const char* OffsetsTable::FieldNameForOffset(intptr_t class_id,
+ intptr_t offset) {
+ return cached_offsets_.LookupValue({class_id, offset});
+}
+
+#define DEFINE_OFFSETS_TABLE_ENTRY(class_name, field_name) \
+ {class_name::kClassId, #field_name, OFFSET_OF(Raw##class_name, field_name)},
+
+// clang-format off
+OffsetsTable::OffsetsTableEntry OffsetsTable::offsets_table[] = {
+ RAW_CLASSES_AND_FIELDS(DEFINE_OFFSETS_TABLE_ENTRY)
+ {-1, nullptr, -1}
+};
+// clang-format on
+
+#undef DEFINE_OFFSETS_TABLE_ENTRY
+
+#endif
+
+} // namespace dart
diff --git a/runtime/vm/raw_object_fields.h b/runtime/vm/raw_object_fields.h
new file mode 100644
index 0000000..9f67643
--- /dev/null
+++ b/runtime/vm/raw_object_fields.h
@@ -0,0 +1,78 @@
+// 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.
+//
+// This file (and "raw_object_fields.cc") provide a kind of reflection that
+// allows us to identify the name of fields in hand-written "Raw..." classes
+// (from "raw_object.h") given the class and the offset within the object. This
+// is used for example by the snapshot profile writer ("v8_snapshot_writer.h")
+// to show the property names of these built-in objects in the snapshot profile.
+
+#ifndef RUNTIME_VM_RAW_OBJECT_FIELDS_H_
+#define RUNTIME_VM_RAW_OBJECT_FIELDS_H_
+
+#include <utility>
+
+#include "vm/hash_map.h"
+#include "vm/object.h"
+#include "vm/raw_object.h"
+
+namespace dart {
+
+#if defined(DART_PRECOMPILER)
+
+class OffsetsTable : public ZoneAllocated {
+ public:
+ explicit OffsetsTable(Zone* zone);
+
+ // Returns 'nullptr' if no offset was found.
+ // Otherwise, the returned string is allocated in global static memory.
+ const char* FieldNameForOffset(intptr_t cid, intptr_t offset);
+
+ private:
+ struct OffsetsTableEntry {
+ const intptr_t class_id;
+ const char* field_name;
+ intptr_t offset;
+ };
+
+ static OffsetsTableEntry offsets_table[];
+
+ struct IntAndIntToStringMapTraits {
+ typedef std::pair<intptr_t, intptr_t> Key;
+ typedef const char* Value;
+
+ struct Pair {
+ Key key;
+ Value value;
+ Pair() : key({-1, -1}), value(nullptr) {}
+ 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) { return key.first ^ key.second; }
+ static bool IsKeyEqual(Pair x, Key y) {
+ return x.key.first == y.first && x.key.second == y.second;
+ }
+ };
+
+ DirectChainedHashMap<IntAndIntToStringMapTraits> cached_offsets_;
+};
+
+#else
+
+class OffsetsTable : public ZoneAllocated {
+ public:
+ explicit OffsetsTable(Zone* zone) {}
+
+ const char* FieldNameForOffset(intptr_t cid, intptr_t offset) {
+ return nullptr;
+ }
+};
+
+#endif
+
+} // namespace dart
+
+#endif // RUNTIME_VM_RAW_OBJECT_FIELDS_H_
diff --git a/runtime/vm/v8_snapshot_writer.cc b/runtime/vm/v8_snapshot_writer.cc
index e945ca0..ce24a25 100644
--- a/runtime/vm/v8_snapshot_writer.cc
+++ b/runtime/vm/v8_snapshot_writer.cc
@@ -57,7 +57,7 @@
info->type = type_id;
if (name != nullptr) {
- info->name = EnsureString(name);
+ info->name = EnsureString(OS::SCreate(zone_, "[%s] %s", type, name));
} else {
info->name = EnsureString(type);
}
@@ -69,18 +69,16 @@
}
void V8SnapshotProfileWriter::AttributeReferenceTo(ObjectId object_id,
- ObjectId to_object_id) {
- EnsureId(to_object_id);
+ Reference reference) {
+ EnsureId(reference.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});
+ ASSERT(reference.offset_or_name >= 0);
+ info->edges->Add({
+ reference.reference_type == Reference::kElement ? kElement : kProperty,
+ reference.offset_or_name,
+ reference.to_object_id,
+ });
++edge_count_;
}
@@ -134,9 +132,8 @@
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(info.type);
+ writer->PrintValue64(info.name_or_index);
writer->PrintValue64(nodes_.LookupValue(info.to_node).offset);
writer->PrintNewline();
}
@@ -235,8 +232,8 @@
writer->OpenArray("edges");
// Write references from the artificial root to the actual roots.
- for (ObjectId root : roots_) {
- WriteEdgeInfo(writer, {root});
+ for (intptr_t i = 0; i < roots_.length(); ++i) {
+ WriteEdgeInfo(writer, {kElement, i, roots_[i]});
}
ObjectIdToNodeInfoTraits::Pair* entry = nullptr;
diff --git a/runtime/vm/v8_snapshot_writer.h b/runtime/vm/v8_snapshot_writer.h
index c506b2e..fa9ee85 100644
--- a/runtime/vm/v8_snapshot_writer.h
+++ b/runtime/vm/v8_snapshot_writer.h
@@ -11,6 +11,7 @@
#include "vm/allocation.h"
#include "vm/hash_map.h"
#include "vm/json_writer.h"
+#include "vm/object.h"
namespace dart {
@@ -29,10 +30,7 @@
static Key KeyOf(Pair pair) { return pair.key; }
- static size_t Hashcode(Key key) {
- /// SAMIR_TODO
- return 0;
- }
+ static size_t Hashcode(Key key) { return String::Hash(key, strlen(key)); }
static bool IsKeyEqual(Pair x, Key y) { return strcmp(x.key, y) == 0; }
};
@@ -51,6 +49,22 @@
typedef std::pair<IdSpace, intptr_t> ObjectId;
+ struct Reference {
+ ObjectId to_object_id;
+ enum {
+ kElement,
+ kProperty,
+ } reference_type;
+ intptr_t offset_or_name;
+ };
+
+ enum ConstantStrings {
+ kUnknownString = 0,
+ kPropertyString = 1,
+ kObjectString = 2,
+ kArtificialRootString = 3,
+ };
+
#if !defined(DART_PRECOMPILER)
explicit V8SnapshotProfileWriter(Zone* zone) {}
virtual ~V8SnapshotProfileWriter() {}
@@ -59,8 +73,9 @@
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 AttributeReferenceTo(ObjectId object_id, Reference reference) {}
void AddRoot(ObjectId object_id) {}
+ intptr_t EnsureString(const char* str) { return 0; }
#else
explicit V8SnapshotProfileWriter(Zone* zone);
virtual ~V8SnapshotProfileWriter() {}
@@ -80,7 +95,7 @@
// 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);
+ void AttributeReferenceTo(ObjectId object_id, Reference reference);
// Marks an object as being a root in the graph. Used for analysis of the
// graph.
@@ -89,12 +104,15 @@
// Write to a file in the V8 Snapshot Profile (JSON/.heapsnapshot) format.
void Write(const char* file);
+ intptr_t EnsureString(const char* str);
+
private:
static constexpr intptr_t kNumNodeFields = 5;
static constexpr intptr_t kNumEdgeFields = 3;
struct EdgeInfo {
- // 'type' and 'name_or_index' aren't supported yet.
+ intptr_t type;
+ intptr_t name_or_index;
ObjectId to_node;
};
@@ -131,18 +149,10 @@
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,
diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni
index b0e7872..a9b02fc 100644
--- a/runtime/vm/vm_sources.gni
+++ b/runtime/vm/vm_sources.gni
@@ -215,6 +215,8 @@
"random.h",
"raw_object.cc",
"raw_object.h",
+ "raw_object_fields.cc",
+ "raw_object_fields.h",
"raw_object_snapshot.cc",
"regexp.cc",
"regexp.h",