Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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:
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();
340 js_.PrintProperty64("word_size", dart::compiler::target::kWordSize);
341 js_.PrintProperty64("compressed_word_size",
342 dart::compiler::target::kCompressedWordSize);
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
ClassPtr At(intptr_t cid) const
void ResetObjectIdTable()
Definition heap.cc:888
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:295
ObjectStore * object_store() const
Definition isolate.h:505
SafepointRwLock * program_lock()
Definition isolate.h:532
ClassTable * class_table() const
Definition isolate.h:491
static intptr_t class_table_offset()
Definition isolate.h:384
static intptr_t cached_class_table_table_offset()
Definition isolate.h:395
static intptr_t object_store_offset()
Definition isolate.h:402
void PrintProperty64(const char *name, int64_t i)
void Steal(char **buffer, intptr_t *buffer_length)
void PrintProperty(const char *name, intptr_t i)
void OpenArray(const char *property_name=nullptr)
void PrintfProperty(const char *name, const char *format,...) PRINTF_ATTRIBUTE(3
void OpenObject(const char *property_name=nullptr)
void PrintValue64(int64_t i)
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
static Thread * Current()
Definition thread.h:361
static intptr_t isolate_offset()
Definition thread.h:534
static intptr_t isolate_group_offset()
Definition thread.h:535
IsolateGroup * isolate_group() const
Definition thread.h:540
static intptr_t dispatch_table_array_offset()
Definition thread.h:844
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)
static const uint8_t buffer[]
uint8_t value
Dart_NativeFunction function
Definition fuchsia.cc:51
constexpr intptr_t kSnapshotAnalyzerVersion
void Dart_DumpSnapshotInformationAsJson(const Dart_SnapshotAnalyzerInformation &info, char **buffer, intptr_t *buffer_length)
const char *const name
uintptr_t uword
Definition globals.h:501
const intptr_t cid
const uintptr_t id