blob: 9588400658aeeb25673ca118112948312d5486c6 [file] [log] [blame]
// Copyright (c) 2020, 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/compiler/aot/precompiler_tracer.h"
#include "vm/compiler/aot/precompiler.h"
#include "vm/zone_text_buffer.h"
namespace dart {
#if defined(DART_PRECOMPILER)
DEFINE_FLAG(charp,
trace_precompiler_to,
nullptr,
"Output machine readable precompilation trace into the given file");
PrecompilerTracer* PrecompilerTracer::StartTracingIfRequested(
Precompiler* precompiler) {
const char* filename = FLAG_trace_precompiler_to;
if (filename == nullptr) {
return nullptr;
}
if ((Dart::file_write_callback() == nullptr) ||
(Dart::file_open_callback() == nullptr) ||
(Dart::file_close_callback() == nullptr)) {
OS::PrintErr("warning: Could not access file callbacks.");
return nullptr;
}
void* file = Dart::file_open_callback()(filename, /*write=*/true);
if (file == NULL) {
OS::PrintErr("warning: Failed to write precompiler trace: %s\n", filename);
return nullptr;
}
return new PrecompilerTracer(precompiler, file);
}
PrecompilerTracer::PrecompilerTracer(Precompiler* precompiler, void* stream)
: zone_(Thread::Current()->zone()),
precompiler_(precompiler),
buffer_(1024),
stream_(stream),
strings_(HashTables::New<StringTable>(1024)),
entities_(HashTables::New<EntityTable>(1024)),
object_(Object::Handle()),
cls_(Class::Handle()) {
Write("{\"trace\":[\"R\",");
}
void PrecompilerTracer::Finalize() {
Write("\"E\"],");
WriteEntityTable();
Write(",");
WriteStringTable();
Write("}\n");
const intptr_t output_length = buffer_.length();
char* output = buffer_.Steal();
Dart::file_write_callback()(output, output_length, stream_);
free(output);
Dart::file_close_callback()(stream_);
strings_.Release();
entities_.Release();
}
void PrecompilerTracer::WriteEntityTable() {
Write("\"entities\":[");
const auto& entities_by_id =
Array::Handle(zone_, Array::New(entities_.NumOccupied()));
EntityTable::Iterator it(&entities_);
while (it.MoveNext()) {
object_ = entities_.GetPayload(it.Current(), 0);
const intptr_t index = Smi::Cast(object_).Value();
object_ = entities_.GetKey(it.Current());
entities_by_id.SetAt(index, object_);
}
auto& obj = Object::Handle(zone_);
auto& lib = Library::Handle(zone_);
auto& str = String::Handle(zone_);
for (intptr_t i = 0; i < entities_by_id.Length(); i++) {
if (i > 0) {
Write(",");
}
obj = entities_by_id.At(i);
if (obj.IsFunction()) {
const auto& fun = Function::Cast(obj);
cls_ = fun.Owner();
const intptr_t selector_id =
precompiler_->selector_map()->SelectorId(fun);
Write("\"%c\",%" Pd ",%" Pd ",%" Pd "",
fun.IsDynamicFunction() ? 'F' : 'S', InternEntity(cls_),
InternString(NameForTrace(fun)), selector_id);
} else if (obj.IsField()) {
const auto& field = Field::Cast(obj);
cls_ = field.Owner();
str = field.name();
Write("\"V\",%" Pd ",%" Pd ",0", InternEntity(cls_), InternString(str));
} else if (obj.IsClass()) {
const auto& cls = Class::Cast(obj);
lib = cls.library();
str = lib.url();
const auto url_id = InternString(str);
str = cls.ScrubbedName();
const auto name_id = InternString(str);
Write("\"C\",%" Pd ",%" Pd ",0", url_id, name_id);
} else {
UNREACHABLE();
}
}
Write("]");
}
void PrecompilerTracer::WriteStringTable() {
Write("\"strings\":[");
GrowableArray<const char*> strings_by_id(strings_.NumOccupied());
strings_by_id.EnsureLength(strings_.NumOccupied(), nullptr);
StringTable::Iterator it(&strings_);
while (it.MoveNext()) {
object_ = strings_.GetPayload(it.Current(), 0);
const auto index = Smi::Cast(object_).Value();
object_ = strings_.GetKey(it.Current());
strings_by_id[index] = String::Cast(object_).ToCString();
}
auto comma = false;
for (auto str : strings_by_id) {
Write("%s\"%s\"", comma ? "," : "", str);
comma = true;
}
Write("]");
}
intptr_t PrecompilerTracer::InternString(const CString& cstr) {
object_ = Smi::New(strings_.NumOccupied());
object_ = strings_.InsertNewOrGetValue(cstr, object_);
return Smi::Cast(object_).Value();
}
intptr_t PrecompilerTracer::InternString(const String& str) {
object_ = Smi::New(strings_.NumOccupied());
object_ = strings_.InsertOrGetValue(str, object_);
return Smi::Cast(object_).Value();
}
intptr_t PrecompilerTracer::InternEntity(const Object& obj) {
ASSERT(obj.IsFunction() || obj.IsClass() || obj.IsField());
const auto num_occupied = entities_.NumOccupied();
object_ = Smi::New(num_occupied);
object_ = entities_.InsertOrGetValue(obj, object_);
const auto id = Smi::Cast(object_).Value();
if (id == num_occupied) {
cls_ = Class::null();
if (obj.IsFunction()) {
cls_ = Function::Cast(obj).Owner();
} else if (obj.IsField()) {
cls_ = Field::Cast(obj).Owner();
}
if (cls_.ptr() != Class::null()) {
InternEntity(cls_);
}
}
return id;
}
PrecompilerTracer::CString PrecompilerTracer::NameForTrace(const Function& f) {
ZoneTextBuffer buffer(zone_);
f.PrintName(NameFormattingParams::DisambiguatedWithoutClassName(
Object::NameVisibility::kInternalName),
&buffer);
return {buffer.buffer(), buffer.length(),
String::Hash(buffer.buffer(), buffer.length())};
}
#endif // defined(DART_PRECOMPILER)
} // namespace dart