1// Copyright (c) 2018, 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#ifndef RUNTIME_VM_V8_SNAPSHOT_WRITER_H_
6#define RUNTIME_VM_V8_SNAPSHOT_WRITER_H_
7
8#include <utility>
9
10#include "platform/assert.h"
11#include "vm/allocation.h"
12#include "vm/hash_map.h"
13#include "vm/hash_table.h"
14#include "vm/json_writer.h"
15#include "vm/object.h"
16
17namespace dart {
18
19struct StringToIntMapTraits {
20 typedef char const* Key;
21 typedef intptr_t Value;
22
23 struct Pair {
24 Key key;
25 Value value;
26 Pair() : key(nullptr), value(-1) {}
27 Pair(Key k, Value v) : key(k), value(v) {}
28 };
29
30 static Value ValueOf(Pair pair) { return pair.value; }
31
32 static Key KeyOf(Pair pair) { return pair.key; }
33
34 static size_t Hashcode(Key key) { return String::Hash(key, strlen(key)); }
35
36 static bool IsKeyEqual(Pair x, Key y) { return strcmp(x.key, y) == 0; }
37};
38
39class V8SnapshotProfileWriter : public ZoneAllocated {
40 public:
41 enum IdSpace {
42 kSnapshot = 0, // Can be VM or Isolate heap, they share ids.
43 kVmText = 1,
44 kIsolateText = 2,
45 kVmData = 3,
46 kIsolateData = 4,
47 kArtificial = 5, // Artificial objects (e.g. the global root).
48 kIdSpaceBits = 3,
49 };
50
51 typedef std::pair<IdSpace, intptr_t> ObjectId;
52
53 struct Reference {
54 ObjectId to_object_id;
55 enum {
56 kElement,
57 kProperty,
58 } reference_type;
59 intptr_t offset_or_name;
60 };
61
62 enum ConstantStrings {
63 kUnknownString = 0,
64 kArtificialRootString = 1,
65 };
66
67#if !defined(DART_PRECOMPILER)
68 explicit V8SnapshotProfileWriter(Zone* zone) {}
69 virtual ~V8SnapshotProfileWriter() {}
70
71 void SetObjectTypeAndName(ObjectId object_id,
72 const char* type,
73 const char* name) {}
74 void AttributeBytesTo(ObjectId object_id, size_t num_bytes) {}
75 void AttributeReferenceTo(ObjectId object_id, Reference reference) {}
76 void AddRoot(ObjectId object_id, const char* name = nullptr) {}
77 intptr_t EnsureString(const char* str) { return 0; }
78#else
79 explicit V8SnapshotProfileWriter(Zone* zone);
80 virtual ~V8SnapshotProfileWriter() {}
81
82 // Records that the object referenced by 'object_id' has type 'type'. The
83 // 'type' for all 'Instance's should be 'Instance', not the user-visible type
84 // and use 'name' for the real type instead.
85 void SetObjectTypeAndName(ObjectId object_id,
86 const char* type,
87 const char* name);
88
89 // Charges 'num_bytes'-many bytes to 'object_id'. In a clustered snapshot,
90 // objects can have their data spread across multiple sections, so this can be
91 // called multiple times for the same object.
92 void AttributeBytesTo(ObjectId object_id, size_t num_bytes);
93
94 // Records that a reference to the object with id 'to_object_id' was written
95 // in order to serialize the object with id 'object_id'. This does not affect
96 // the number of bytes charged to 'object_id'.
97 void AttributeReferenceTo(ObjectId object_id, Reference reference);
98
99 // Marks an object as being a root in the graph. Used for analysis of the
100 // graph.
101 void AddRoot(ObjectId object_id, const char* name = nullptr);
102
103 // Write to a file in the V8 Snapshot Profile (JSON/.heapsnapshot) format.
104 void Write(const char* file);
105
106 intptr_t EnsureString(const char* str);
107
108 static ObjectId ArtificialRootId() { return {kArtificial, 0}; }
109
110 private:
111 static constexpr intptr_t kNumNodeFields = 5;
112 static constexpr intptr_t kNumEdgeFields = 3;
113
114 struct EdgeInfo {
115 intptr_t type;
116 intptr_t name_or_index;
117 ObjectId to_node;
118 };
119
120 struct NodeInfo {
121 intptr_t type;
122 intptr_t name;
123 ObjectId id;
124 intptr_t self_size;
125 ZoneGrowableArray<EdgeInfo>* edges = nullptr;
126 // Populated during serialization.
127 intptr_t offset = -1;
128 // 'trace_node_id' isn't supported.
129 // 'edge_count' is computed on-demand.
130
131 // Used for testing sentinel in the hashtable.
132 bool operator!=(const NodeInfo& other) { return id != other.id; }
133 bool operator==(const NodeInfo& other) { return !(*this != other); }
134
135 NodeInfo(intptr_t type,
136 intptr_t name,
137 ObjectId id,
138 intptr_t self_size,
139 ZoneGrowableArray<EdgeInfo>* edges,
140 intptr_t offset)
141 : type(type),
142 name(name),
143 id(id),
144 self_size(self_size),
145 edges(edges),
146 offset(offset) {}
147 };
148
149 NodeInfo DefaultNode(ObjectId object_id);
150 const NodeInfo& ArtificialRoot();
151
152 NodeInfo* EnsureId(ObjectId object_id);
153 static intptr_t NodeIdFor(ObjectId id) {
154 return (id.second << kIdSpaceBits) | id.first;
155 }
156
157 enum ConstantEdgeTypes {
158 kContext = 0,
159 kElement = 1,
160 kProperty = 2,
161 kInternal = 3,
162 kHidden = 4,
163 kShortcut = 5,
164 kWeak = 6,
165 kExtra = 7,
166 };
167
168 enum ConstantNodeTypes {
169 kUnknown = 0,
170 kArtificialRoot = 1,
171 };
172
173 struct ObjectIdToNodeInfoTraits {
174 typedef ObjectId Key;
175 typedef NodeInfo Value;
176
177 struct Pair {
178 Key key;
179 Value value;
180 Pair()
181 : key{kSnapshot, -1}, value{0, 0, {kSnapshot, -1}, 0, nullptr, -1} {};
182 Pair(Key k, Value v) : key(k), value(v) {}
183 };
184
185 static Key KeyOf(const Pair& pair) { return pair.key; }
186
187 static Value ValueOf(const Pair& pair) { return pair.value; }
188
189 static size_t Hashcode(Key key) { return NodeIdFor(key); }
190
191 static bool IsKeyEqual(const Pair& x, Key y) { return x.key == y; }
192 };
193
194 Zone* zone_;
195 void Write(JSONWriter* writer);
196 void WriteNodeInfo(JSONWriter* writer, const NodeInfo& info);
197 void WriteEdgeInfo(JSONWriter* writer, const EdgeInfo& info);
198 void WriteStringsTable(JSONWriter* writer,
199 const DirectChainedHashMap<StringToIntMapTraits>& map);
200
201 DirectChainedHashMap<ObjectIdToNodeInfoTraits> nodes_;
202 DirectChainedHashMap<StringToIntMapTraits> node_types_;
203 DirectChainedHashMap<StringToIntMapTraits> edge_types_;
204 DirectChainedHashMap<StringToIntMapTraits> strings_;
205
206 // We don't have a zone-allocated hash set, so we just re-use the type for
207 // nodes_ even though we don't need to access the node info (and fill it with
208 // dummy values).
209 DirectChainedHashMap<ObjectIdToNodeInfoTraits> roots_;
210
211 size_t edge_count_ = 0;
212#endif
213};
214
215} // namespace dart
216
217#endif // RUNTIME_VM_V8_SNAPSHOT_WRITER_H_
218