Flutter Engine
The Flutter Engine
analyze_snapshot_api_impl.cc
Go to the documentation of this file.
1// Copyright (c) 2021, 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 <map>
6#include <set>
7#include <sstream>
8
11#include "vm/dart_api_impl.h"
12#include "vm/json_writer.h"
13#include "vm/object.h"
14#include "vm/object_store.h"
15#include "vm/thread.h"
16
17namespace dart {
18namespace snapshot_analyzer {
19
20constexpr intptr_t kSnapshotAnalyzerVersion = 2;
21constexpr intptr_t kStartIndex = 1;
22
24 public:
27
28 void init(std::function<void(ObjectPtr)>* fun) { callback_ = fun; }
29
30 void VisitPointers(ObjectPtr* first, ObjectPtr* last) override {
31 for (ObjectPtr* current = first; current <= last; current++) {
32 (*callback_)(*current);
33 }
34 }
35
36#if defined(DART_COMPRESSED_POINTERS)
37 void VisitCompressedPointers(uword heap_base,
39 CompressedObjectPtr* last) override {
40 for (CompressedObjectPtr* current = first; current <= last; current++) {
41 (*callback_)(current->Decompress(heap_base));
42 }
43 }
44#endif
45
46 private:
47 std::function<void(ObjectPtr object)>* callback_ = nullptr;
48};
49
51 public:
53 : info_(info) {}
54
55 // Saves JSON format snapshot information in the output character buffer.
56 void DumpSnapshotInformation(char** buffer, intptr_t* buffer_length);
57
58 private:
59 void DumpLibrary(const Library& library);
60 void DumpArray(const Array& array, const char* name);
61 void DumpClass(const Class& klass);
62 void DumpFunction(const Function& function);
63 void DumpCode(const Code& code);
64 void DumpField(const Field& field);
65 void DumpString(const String& string);
66 void DumpInstance(const Object& object);
67 void DumpType(const Type& type);
68 void DumpObjectPool(const ObjectPool& pool);
69
70 void DumpInterestingObjects();
71 void DumpMetadata();
72
73 intptr_t GetObjectId(ObjectPtr obj) { return heap_->GetObjectId(obj); }
74
75 const Dart_SnapshotAnalyzerInformation& info_;
76 JSONWriter js_;
77 Thread* thread_;
78 Heap* heap_;
79};
80
81void SnapshotAnalyzer::DumpLibrary(const Library& library) {
82 js_.PrintProperty("type", "Library");
83 js_.PrintProperty("url", String::Handle(library.url()).ToCString());
84
85 js_.PrintProperty("toplevel_class",
86 GetObjectId(Object::RawCast(library.toplevel_class())));
87}
88
89void SnapshotAnalyzer::DumpArray(const Array& array, const char* name) {
90 js_.OpenArray(name);
91 for (intptr_t i = 0; i < array.Length(); ++i) {
92 js_.PrintValue64(GetObjectId(array.At(i)));
93 }
94 js_.CloseArray();
95}
96
97void SnapshotAnalyzer::DumpClass(const Class& klass) {
98 js_.PrintProperty("type", "Class");
99
100 js_.PrintProperty("class_id", klass.id());
101 js_.PrintProperty("name", String::Handle(klass.Name()).ToCString());
102 js_.PrintProperty("super_class", GetObjectId(klass.SuperClass()));
103
104 Zone* zone = thread_->zone();
105 Array& array = Array::Handle(zone);
106
107 // To avoid depending on layout of VM internal classes we don't use
108 // js_.PrintProperty("fields", GetObjectId(heap, klass.fields());
109 // here and instead iterate and refer to them manually.
110 array = klass.fields();
111 if (!array.IsNull()) {
112 DumpArray(array, "fields");
113 }
114
115 array = klass.functions();
116 if (!array.IsNull()) {
117 DumpArray(array, "functions");
118 }
119
120 array = klass.interfaces();
121 if (!array.IsNull()) {
122 DumpArray(array, "interfaces");
123 }
124
125 Library& library = Library::Handle(klass.library());
126 if (!library.IsNull()) {
127 js_.PrintProperty("library", GetObjectId(klass.library()));
128 }
129}
130
131void SnapshotAnalyzer::DumpFunction(const Function& function) {
132 js_.PrintProperty("type", "Function");
133 js_.PrintProperty("name", function.ToCString());
134
135 js_.PrintProperty("signature",
136 String::Handle(function.InternalSignature()).ToCString());
137
138 js_.PrintProperty("code", GetObjectId(function.CurrentCode()));
139 if (function.IsClosureFunction()) {
140 js_.PrintProperty("parent_function",
141 GetObjectId(function.parent_function()));
142 }
143}
144
145void SnapshotAnalyzer::DumpCode(const Code& code) {
146 js_.PrintProperty("type", "Code");
147 const auto instruction_base =
148 reinterpret_cast<uint64_t>(info_.vm_isolate_instructions);
149
150 // On different architectures the type of the underlying
151 // dart::uword can result in an unsigned long long vs unsigned long
152 // mismatch.
153 const auto code_addr = static_cast<uint64_t>(code.PayloadStart());
154 // Invoking code.PayloadStart() for _kDartVmSnapshotInstructions
155 // when the tree has been shaken always returns 0
156 if (code_addr == 0) {
157 js_.PrintProperty64("offset", 0);
158 js_.PrintProperty64("size", 0);
159 js_.PrintProperty("section", "_kDartVmSnapshotInstructions");
160 } else {
161 js_.PrintProperty64("offset", code_addr - instruction_base);
162 js_.PrintProperty64("size", static_cast<uint64_t>(code.Size()));
163 js_.PrintProperty("section", "_kDartIsolateSnapshotInstructions");
164 }
165}
166
167void SnapshotAnalyzer::DumpField(const Field& field) {
168 js_.PrintProperty("type", "Field");
169 js_.PrintProperty("name", String::Handle(field.name()).ToCString());
170 js_.PrintProperty64("type_class", GetObjectId(field.type()));
171 if (field.is_static()) {
172 js_.PrintProperty("instance", GetObjectId(field.StaticValue()));
173 }
174 if (field.HasInitializerFunction()) {
175 js_.PrintProperty("initializer_function",
176 GetObjectId(field.InitializerFunction()));
177 }
178}
179
180void SnapshotAnalyzer::DumpString(const String& string) {
181 js_.PrintProperty("type", "String");
182 js_.PrintProperty("value", string.ToCString());
183}
184
185void SnapshotAnalyzer::DumpInstance(const Object& object) {
186 js_.PrintProperty("type", "Instance");
187
188 js_.PrintProperty("class", GetObjectId(object.clazz()));
189
190 FieldVisitor visitor(thread_->isolate_group());
191 // Two phase algorithm, first discover all relevant objects
192 // and assign ids, then write them out.
193 std::function<void(ObjectPtr)> print_reference = [&](ObjectPtr value) {
194 if (!value.IsHeapObject()) return;
195 intptr_t index = GetObjectId(value);
196 js_.PrintValue64(index);
197 };
198 visitor.init(&print_reference);
199
200 js_.OpenArray("references");
201 object.ptr().untag()->VisitPointers(&visitor);
202 js_.CloseArray();
203}
204
205void SnapshotAnalyzer::DumpType(const Type& type) {
206 js_.PrintProperty("type", "Type");
207
208 js_.PrintProperty("type_class", GetObjectId(type.type_class()));
209
210 const TypeArguments& arguments = TypeArguments::Handle(type.arguments());
211 js_.OpenArray("type_arguments");
212 for (intptr_t i = 0; i < arguments.Length(); ++i) {
213 js_.PrintValue64(GetObjectId(arguments.TypeAt(i)));
214 }
215 js_.CloseArray();
216}
217
218void SnapshotAnalyzer::DumpObjectPool(const ObjectPool& pool) {
219 js_.PrintProperty("type", "ObjectPool");
220 js_.OpenArray("references");
221 for (intptr_t i = 0; i < pool.Length(); ++i) {
222 if (pool.TypeAt(i) == ObjectPool::EntryType::kTaggedObject) {
223 // We write (index, offset, value) triplets.
224 js_.PrintValue64(i);
225 js_.PrintValue64(pool.OffsetFromIndex(i));
226 js_.PrintValue64(GetObjectId(pool.ObjectAt(i)));
227 }
228 }
229 js_.CloseArray();
230}
231
232void SnapshotAnalyzer::DumpInterestingObjects() {
233 Zone* zone = thread_->zone();
234
235 heap_->ResetObjectIdTable();
236 std::vector<const Object*> discovered_objects;
237 Object& object = Object::Handle(zone);
238 {
239 NoSafepointScope ns(thread_);
240
241 FieldVisitor visitor(thread_->isolate_group());
242 std::function<void(ObjectPtr)> handle_object = [&](ObjectPtr value) {
243 if (!value.IsHeapObject()) return;
244
245 // Ensure we never handle an object more than once.
246 if (heap_->GetObjectId(value) != 0) return;
247
248 heap_->SetObjectId(value, kStartIndex + discovered_objects.size());
249 discovered_objects.push_back(&Object::Handle(zone, value));
250
251 // Ensure all references of this object are visited first.
252 value->untag()->VisitPointers(&visitor);
253 };
254 visitor.init(&handle_object);
255
256 // BEGIN Visit all things we are interested in.
257
258 // - All constants reachable via object pool
259 object = thread_->isolate_group()->object_store()->global_object_pool();
260 handle_object(object.ptr());
261
262 // - All libraries
263 object = thread_->isolate_group()->object_store()->libraries();
264 object = GrowableObjectArray::Cast(object).data();
265 object.ptr().untag()->VisitPointers(&visitor);
266
267 // - All classes
268 auto class_table = thread_->isolate_group()->class_table();
269 for (intptr_t cid = 0; cid < class_table->NumCids(); ++cid) {
270 if (!class_table->HasValidClassAt(cid)) continue;
271 object = class_table->At(cid);
272 handle_object(object.ptr());
273 }
274 }
275
276 // Print information about objects
277 js_.OpenArray("objects");
278
279 // The 0 object id is used in the VM's weak hashmap implementation
280 // to indicate no value.
281 js_.OpenObject();
282 js_.PrintProperty("type", "NoValue");
283 js_.CloseObject();
284
285 for (size_t id = 0; id < discovered_objects.size(); ++id) {
286 const auto* object = discovered_objects[id];
287 js_.OpenObject();
288 // TODO(balid): Remove this as it can be inferred from the array position.
289 // Used for manual debugging at the moment.
290 js_.PrintProperty64("id", id + kStartIndex);
291 // Order matters here, Strings are a subtype of Instance, for example.
292 if (object->IsNull()) {
293 js_.PrintProperty("type", "Null");
294 } else if (object->IsLibrary()) {
295 DumpLibrary(Library::Cast(*object));
296 } else if (object->IsObjectPool()) {
297 DumpObjectPool(ObjectPool::Cast(*object));
298 } else if (object->IsClass()) {
299 DumpClass(Class::Cast(*object));
300 } else if (object->IsFunction()) {
301 DumpFunction(Function::Cast(*object));
302 } else if (object->IsCode()) {
303 DumpCode(Code::Cast(*object));
304 } else if (object->IsField()) {
305 DumpField(Field::Cast(*object));
306 } else if (object->IsString()) {
307 DumpString(String::Cast(*object));
308 } else if (object->IsArray()) {
309 js_.PrintProperty("type", "Array");
310 const Array& array = Array::Handle(Array::RawCast(object->ptr()));
311 DumpArray(array, "elements");
312 } else if (object->IsType()) {
313 DumpType(Type::Cast(*object));
314 } else if (object->IsInstance()) {
315 DumpInstance(*object);
316 }
317
318 js_.CloseObject();
319 }
320 js_.CloseArray();
321}
322
323void SnapshotAnalyzer::DumpMetadata() {
324 js_.OpenObject("metadata");
325 js_.OpenObject("offsets");
326 js_.OpenObject("thread");
327 // TODO(balid): Use `dart::compiler::target::` versions.
328 js_.PrintProperty("isolate", Thread::isolate_offset());
329 js_.PrintProperty("isolate_group", Thread::isolate_group_offset());
330 js_.PrintProperty("dispatch_table_array",
332 js_.CloseObject();
333 js_.OpenObject("isolate_group");
335 js_.PrintProperty("cached_class_table",
337 js_.PrintProperty("object_store_offset", IsolateGroup::object_store_offset());
338 js_.CloseObject();
339 js_.CloseObject();
341 js_.PrintProperty64("compressed_word_size",
343 js_.PrintProperty64("analyzer_version", kSnapshotAnalyzerVersion);
344 js_.CloseObject();
345}
346
347// TODO(#47924): Add processing of the entires in the dispatch table.
348// Below is an example skeleton
349// void DumpDispatchTable(dart::Thread* thread) {
350// auto dispatch = thread->isolate_group()->dispatch_table();
351// auto length = dispatch->length();
352// We must unbias the array entries so we don't crash on null access.
353// auto entries = dispatch->ArrayOrigin() - DispatchTable::kOriginElement;
354// for (intptr_t i = 0; i < length; i++) {
355// OS::Print("0x%lx at %ld\n", entries[i], i);
356// }
357// }
358
360 intptr_t* buffer_length) {
361 thread_ = Thread::Current();
362 heap_ = thread_->isolate_group()->heap();
363 DARTSCOPE(thread_);
364
365 // Open empty object so output is valid/parsable JSON.
366 js_.OpenObject();
367 js_.OpenObject("snapshot_data");
368 // Base addresses of the snapshot data, useful to calculate relative offsets.
369 js_.PrintfProperty("vm_data", "%p", info_.vm_snapshot_data);
370 js_.PrintfProperty("vm_instructions", "%p", info_.vm_snapshot_instructions);
371 js_.PrintfProperty("isolate_data", "%p", info_.vm_isolate_data);
372 js_.PrintfProperty("isolate_instructions", "%p",
374 js_.CloseObject();
375
376 {
377 // Debug builds assert that our thread has a lock before accessing
378 // vm internal fields.
379 SafepointReadRwLocker ml(thread_, thread_->isolate_group()->program_lock());
380 DumpInterestingObjects();
381 DumpMetadata();
382 }
383
384 // Close our empty object.
385 js_.CloseObject();
386
387 // Give ownership to caller.
388 js_.Steal(buffer, buffer_length);
389}
390
393 char** out,
394 intptr_t* out_len) {
395 SnapshotAnalyzer analyzer(info);
396 analyzer.DumpSnapshotInformation(out, out_len);
397}
398
399} // namespace snapshot_analyzer
400} // namespace dart
AutoreleasePool pool
static void info(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: DM.cpp:213
GLenum type
ClassPtr At(intptr_t cid) const
Definition: class_table.h:362
void ResetObjectIdTable()
Definition: heap.cc:899
intptr_t GetObjectId(ObjectPtr raw_obj) const
Definition: heap.h:197
void SetObjectId(ObjectPtr raw_obj, intptr_t object_id)
Definition: heap.h:193
Heap * heap() const
Definition: isolate.h:296
ObjectStore * object_store() const
Definition: isolate.h:510
SafepointRwLock * program_lock()
Definition: isolate.h:537
ClassTable * class_table() const
Definition: isolate.h:496
static intptr_t class_table_offset()
Definition: isolate.h:385
static intptr_t cached_class_table_table_offset()
Definition: isolate.h:396
static intptr_t object_store_offset()
Definition: isolate.h:403
void PrintProperty64(const char *name, int64_t i)
Definition: json_writer.cc:234
void Steal(char **buffer, intptr_t *buffer_length)
Definition: json_writer.cc:300
void PrintProperty(const char *name, intptr_t i)
Definition: json_writer.cc:229
void OpenArray(const char *property_name=nullptr)
Definition: json_writer.cc:112
void PrintfProperty(const char *name, const char *format,...) PRINTF_ATTRIBUTE(3
Definition: json_writer.cc:269
void OpenObject(const char *property_name=nullptr)
Definition: json_writer.cc:89
void PrintValue64(int64_t i)
Definition: json_writer.cc:143
IsolateGroup * isolate_group() const
Definition: visitor.h:25
void VisitCompressedPointers(uword heap_base, CompressedObjectPtr *first, CompressedObjectPtr *last)
Definition: visitor.h:43
virtual const char * ToCString() const
Definition: object.h:366
static Object & Handle()
Definition: object.h:407
static ObjectPtr RawCast(ObjectPtr obj)
Definition: object.h:325
Zone * zone() const
Definition: thread_state.h:37
static Thread * Current()
Definition: thread.h:362
static intptr_t isolate_offset()
Definition: thread.h:535
static intptr_t isolate_group_offset()
Definition: thread.h:536
IsolateGroup * isolate_group() const
Definition: thread.h:541
static intptr_t dispatch_table_array_offset()
Definition: thread.h:857
void init(std::function< void(ObjectPtr)> *fun)
void VisitPointers(ObjectPtr *first, ObjectPtr *last) override
void DumpSnapshotInformation(char **buffer, intptr_t *buffer_length)
SnapshotAnalyzer(const Dart_SnapshotAnalyzerInformation &info)
#define DARTSCOPE(thread)
Definition: dart_api_impl.h:77
uint8_t value
Dart_NativeFunction function
Definition: fuchsia.cc:51
static constexpr intptr_t kWordSize
Definition: runtime_api.h:274
static constexpr intptr_t kCompressedWordSize
Definition: runtime_api.h:286
constexpr intptr_t kSnapshotAnalyzerVersion
void Dart_DumpSnapshotInformationAsJson(const Dart_SnapshotAnalyzerInformation &info, char **buffer, intptr_t *buffer_length)
Definition: dart_vm.cc:33
const char *const name
uintptr_t uword
Definition: globals.h:501
const intptr_t cid
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
Definition: switches.h:126
const uintptr_t id