| 1 | // Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #include "vm/compiler/aot/precompiler_tracer.h" |
| 6 | |
| 7 | #include "vm/compiler/aot/precompiler.h" |
| 8 | #include "vm/zone_text_buffer.h" |
| 9 | |
| 10 | namespace dart { |
| 11 | |
| 12 | #if defined(DART_PRECOMPILER) |
| 13 | |
| 14 | DEFINE_FLAG(charp, |
| 15 | trace_precompiler_to, |
| 16 | nullptr, |
| 17 | "Output machine readable precompilation trace into the given file" ); |
| 18 | |
| 19 | PrecompilerTracer* PrecompilerTracer::StartTracingIfRequested( |
| 20 | Precompiler* precompiler) { |
| 21 | if (FLAG_trace_precompiler_to != nullptr && |
| 22 | Dart::file_write_callback() != nullptr && |
| 23 | Dart::file_open_callback() != nullptr && |
| 24 | Dart::file_close_callback() != nullptr) { |
| 25 | return new PrecompilerTracer( |
| 26 | precompiler, Dart::file_open_callback()(FLAG_trace_precompiler_to, |
| 27 | /*write=*/true)); |
| 28 | } |
| 29 | return nullptr; |
| 30 | } |
| 31 | |
| 32 | PrecompilerTracer::PrecompilerTracer(Precompiler* precompiler, void* stream) |
| 33 | : zone_(Thread::Current()->zone()), |
| 34 | precompiler_(precompiler), |
| 35 | stream_(stream), |
| 36 | strings_(HashTables::New<StringTable>(1024)), |
| 37 | entities_(HashTables::New<EntityTable>(1024)), |
| 38 | object_(Object::Handle()), |
| 39 | cls_(Class::Handle()) { |
| 40 | Write("{\"trace\":[\"R\"," ); |
| 41 | } |
| 42 | |
| 43 | void PrecompilerTracer::Finalize() { |
| 44 | Write("\"E\"]," ); |
| 45 | WriteEntityTable(); |
| 46 | Write("," ); |
| 47 | WriteStringTable(); |
| 48 | Write("}\n" ); |
| 49 | Dart::file_close_callback()(stream_); |
| 50 | |
| 51 | strings_.Release(); |
| 52 | entities_.Release(); |
| 53 | } |
| 54 | |
| 55 | void PrecompilerTracer::WriteEntityTable() { |
| 56 | Write("\"entities\":[" ); |
| 57 | const auto& entities_by_id = |
| 58 | Array::Handle(zone_, Array::New(entities_.NumOccupied())); |
| 59 | |
| 60 | EntityTable::Iterator it(&entities_); |
| 61 | while (it.MoveNext()) { |
| 62 | object_ = entities_.GetPayload(it.Current(), 0); |
| 63 | const intptr_t index = Smi::Cast(object_).Value(); |
| 64 | object_ = entities_.GetKey(it.Current()); |
| 65 | entities_by_id.SetAt(index, object_); |
| 66 | } |
| 67 | |
| 68 | auto& obj = Object::Handle(zone_); |
| 69 | auto& lib = Library::Handle(zone_); |
| 70 | auto& str = String::Handle(zone_); |
| 71 | for (intptr_t i = 0; i < entities_by_id.Length(); i++) { |
| 72 | if (i > 0) { |
| 73 | Write("," ); |
| 74 | } |
| 75 | obj = entities_by_id.At(i); |
| 76 | if (obj.IsFunction()) { |
| 77 | const auto& fun = Function::Cast(obj); |
| 78 | cls_ = fun.Owner(); |
| 79 | const intptr_t selector_id = |
| 80 | FLAG_use_bare_instructions && FLAG_use_table_dispatch |
| 81 | ? precompiler_->selector_map()->SelectorId(fun) |
| 82 | : -1; |
| 83 | Write("\"%c\",%" Pd ",%" Pd ",%" Pd "" , |
| 84 | fun.IsDynamicFunction() ? 'F' : 'S', InternEntity(cls_), |
| 85 | InternString(NameForTrace(fun)), selector_id); |
| 86 | } else if (obj.IsField()) { |
| 87 | const auto& field = Field::Cast(obj); |
| 88 | cls_ = field.Owner(); |
| 89 | str = field.name(); |
| 90 | Write("\"V\",%" Pd ",%" Pd ",0" , InternEntity(cls_), InternString(str)); |
| 91 | } else if (obj.IsClass()) { |
| 92 | const auto& cls = Class::Cast(obj); |
| 93 | lib = cls.library(); |
| 94 | str = lib.url(); |
| 95 | const auto url_id = InternString(str); |
| 96 | str = cls.ScrubbedName(); |
| 97 | const auto name_id = InternString(str); |
| 98 | Write("\"C\",%" Pd ",%" Pd ",0" , url_id, name_id); |
| 99 | } else { |
| 100 | UNREACHABLE(); |
| 101 | } |
| 102 | } |
| 103 | Write("]" ); |
| 104 | } |
| 105 | |
| 106 | void PrecompilerTracer::WriteStringTable() { |
| 107 | Write("\"strings\":[" ); |
| 108 | GrowableArray<const char*> strings_by_id(strings_.NumOccupied()); |
| 109 | strings_by_id.EnsureLength(strings_.NumOccupied(), nullptr); |
| 110 | StringTable::Iterator it(&strings_); |
| 111 | while (it.MoveNext()) { |
| 112 | object_ = strings_.GetPayload(it.Current(), 0); |
| 113 | const auto index = Smi::Cast(object_).Value(); |
| 114 | object_ = strings_.GetKey(it.Current()); |
| 115 | strings_by_id[index] = String::Cast(object_).ToCString(); |
| 116 | } |
| 117 | auto comma = false; |
| 118 | for (auto str : strings_by_id) { |
| 119 | Write("%s\"%s\"" , comma ? "," : "" , str); |
| 120 | comma = true; |
| 121 | } |
| 122 | Write("]" ); |
| 123 | } |
| 124 | |
| 125 | intptr_t PrecompilerTracer::InternString(const CString& cstr) { |
| 126 | object_ = Smi::New(strings_.NumOccupied()); |
| 127 | object_ = strings_.InsertNewOrGetValue(cstr, object_); |
| 128 | return Smi::Cast(object_).Value(); |
| 129 | } |
| 130 | |
| 131 | intptr_t PrecompilerTracer::InternString(const String& str) { |
| 132 | object_ = Smi::New(strings_.NumOccupied()); |
| 133 | object_ = strings_.InsertOrGetValue(str, object_); |
| 134 | return Smi::Cast(object_).Value(); |
| 135 | } |
| 136 | |
| 137 | intptr_t PrecompilerTracer::InternEntity(const Object& obj) { |
| 138 | ASSERT(obj.IsFunction() || obj.IsClass() || obj.IsField()); |
| 139 | const auto num_occupied = entities_.NumOccupied(); |
| 140 | object_ = Smi::New(num_occupied); |
| 141 | object_ = entities_.InsertOrGetValue(obj, object_); |
| 142 | const auto id = Smi::Cast(object_).Value(); |
| 143 | if (id == num_occupied) { |
| 144 | cls_ = Class::null(); |
| 145 | if (obj.IsFunction()) { |
| 146 | cls_ = Function::Cast(obj).Owner(); |
| 147 | } else if (obj.IsField()) { |
| 148 | cls_ = Field::Cast(obj).Owner(); |
| 149 | } |
| 150 | if (cls_.raw() != Class::null()) { |
| 151 | InternEntity(cls_); |
| 152 | } |
| 153 | } |
| 154 | return id; |
| 155 | } |
| 156 | |
| 157 | PrecompilerTracer::CString PrecompilerTracer::NameForTrace(const Function& f) { |
| 158 | ZoneTextBuffer buffer(zone_); |
| 159 | f.PrintName(NameFormattingParams::DisambiguatedWithoutClassName( |
| 160 | Object::NameVisibility::kInternalName), |
| 161 | &buffer); |
| 162 | return {buffer.buffer(), buffer.length(), |
| 163 | String::Hash(buffer.buffer(), buffer.length())}; |
| 164 | } |
| 165 | |
| 166 | #endif // defined(DART_PRECOMPILER) |
| 167 | |
| 168 | } // namespace dart |
| 169 | |