blob: 9a91ad670ab56658efc0939c0ea060d1a480a47a [file] [log] [blame]
ulan@chromium.org65a89c22012-02-14 11:46:07 +00001// Copyright 2012 the V8 project authors. All rights reserved.
fschneider@chromium.org086aac62010-03-17 13:18:24 +00002// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28#include "v8.h"
fschneider@chromium.orgfb144a02011-05-04 12:43:48 +000029
30#include "profile-generator-inl.h"
31
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000032#include "global-handles.h"
whesse@chromium.orgb08986c2011-03-14 16:13:42 +000033#include "heap-profiler.h"
ager@chromium.org2cc82ae2010-06-14 07:35:38 +000034#include "scopeinfo.h"
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +000035#include "unicode.h"
ager@chromium.org2cc82ae2010-06-14 07:35:38 +000036#include "zone-inl.h"
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +000037#include "debug.h"
fschneider@chromium.org086aac62010-03-17 13:18:24 +000038
fschneider@chromium.org086aac62010-03-17 13:18:24 +000039namespace v8 {
40namespace internal {
41
42
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000043TokenEnumerator::TokenEnumerator()
44 : token_locations_(4),
45 token_removed_(4) {
46}
47
48
49TokenEnumerator::~TokenEnumerator() {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000050 Isolate* isolate = Isolate::Current();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000051 for (int i = 0; i < token_locations_.length(); ++i) {
52 if (!token_removed_[i]) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000053 isolate->global_handles()->ClearWeakness(token_locations_[i]);
54 isolate->global_handles()->Destroy(token_locations_[i]);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000055 }
56 }
57}
58
59
60int TokenEnumerator::GetTokenId(Object* token) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000061 Isolate* isolate = Isolate::Current();
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +000062 if (token == NULL) return TokenEnumerator::kNoSecurityToken;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000063 for (int i = 0; i < token_locations_.length(); ++i) {
64 if (*token_locations_[i] == token && !token_removed_[i]) return i;
65 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000066 Handle<Object> handle = isolate->global_handles()->Create(token);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000067 // handle.location() points to a memory cell holding a pointer
68 // to a token object in the V8's heap.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +000069 isolate->global_handles()->MakeWeak(handle.location(), this,
70 TokenRemovedCallback);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000071 token_locations_.Add(handle.location());
72 token_removed_.Add(false);
73 return token_locations_.length() - 1;
74}
75
76
77void TokenEnumerator::TokenRemovedCallback(v8::Persistent<v8::Value> handle,
78 void* parameter) {
79 reinterpret_cast<TokenEnumerator*>(parameter)->TokenRemoved(
80 Utils::OpenHandle(*handle).location());
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +000081 handle.Dispose();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000082}
83
84
85void TokenEnumerator::TokenRemoved(Object** token_location) {
86 for (int i = 0; i < token_locations_.length(); ++i) {
87 if (token_locations_[i] == token_location && !token_removed_[i]) {
88 token_removed_[i] = true;
89 return;
90 }
91 }
92}
93
94
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +000095StringsStorage::StringsStorage()
96 : names_(StringsMatch) {
97}
98
99
100StringsStorage::~StringsStorage() {
101 for (HashMap::Entry* p = names_.Start();
102 p != NULL;
103 p = names_.Next(p)) {
104 DeleteArray(reinterpret_cast<const char*>(p->value));
105 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000106}
107
108
109const char* StringsStorage::GetCopy(const char* src) {
110 int len = static_cast<int>(strlen(src));
111 Vector<char> dst = Vector<char>::New(len + 1);
112 OS::StrNCpy(dst, src, len);
113 dst[len] = '\0';
rossberg@chromium.orgfab14982012-01-05 15:02:15 +0000114 uint32_t hash =
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000115 HashSequentialString(dst.start(), len, HEAP->HashSeed());
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000116 return AddOrDisposeString(dst.start(), hash);
117}
118
119
120const char* StringsStorage::GetFormatted(const char* format, ...) {
121 va_list args;
122 va_start(args, format);
123 const char* result = GetVFormatted(format, args);
124 va_end(args);
125 return result;
126}
127
128
129const char* StringsStorage::AddOrDisposeString(char* str, uint32_t hash) {
130 HashMap::Entry* cache_entry = names_.Lookup(str, hash, true);
131 if (cache_entry->value == NULL) {
132 // New entry added.
133 cache_entry->value = str;
134 } else {
135 DeleteArray(str);
136 }
137 return reinterpret_cast<const char*>(cache_entry->value);
138}
139
140
141const char* StringsStorage::GetVFormatted(const char* format, va_list args) {
142 Vector<char> str = Vector<char>::New(1024);
143 int len = OS::VSNPrintF(str, format, args);
144 if (len == -1) {
145 DeleteArray(str.start());
146 return format;
147 }
rossberg@chromium.orgfab14982012-01-05 15:02:15 +0000148 uint32_t hash = HashSequentialString(
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000149 str.start(), len, HEAP->HashSeed());
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000150 return AddOrDisposeString(str.start(), hash);
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000151}
152
153
154const char* StringsStorage::GetName(String* name) {
155 if (name->IsString()) {
danno@chromium.orgc612e022011-11-10 11:38:15 +0000156 int length = Min(kMaxNameSize, name->length());
157 SmartArrayPointer<char> data =
158 name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL, 0, length);
rossberg@chromium.orgfab14982012-01-05 15:02:15 +0000159 uint32_t hash =
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000160 HashSequentialString(*data, length, name->GetHeap()->HashSeed());
danno@chromium.orgc612e022011-11-10 11:38:15 +0000161 return AddOrDisposeString(data.Detach(), hash);
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000162 }
163 return "";
164}
165
166
vegorov@chromium.org42841962010-10-18 11:18:59 +0000167const char* StringsStorage::GetName(int index) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000168 return GetFormatted("%d", index);
vegorov@chromium.org42841962010-10-18 11:18:59 +0000169}
170
171
mmassi@chromium.org7028c052012-06-13 11:51:58 +0000172size_t StringsStorage::GetUsedMemorySize() const {
173 size_t size = sizeof(*this);
174 size += sizeof(HashMap::Entry) * names_.capacity();
175 for (HashMap::Entry* p = names_.Start(); p != NULL; p = names_.Next(p)) {
176 size += strlen(reinterpret_cast<const char*>(p->value)) + 1;
177 }
178 return size;
179}
180
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000181const char* const CodeEntry::kEmptyNamePrefix = "";
lrn@chromium.org25156de2010-04-06 13:10:27 +0000182
183
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000184void CodeEntry::CopyData(const CodeEntry& source) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000185 tag_ = source.tag_;
186 name_prefix_ = source.name_prefix_;
187 name_ = source.name_;
188 resource_name_ = source.resource_name_;
189 line_number_ = source.line_number_;
190}
191
192
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000193uint32_t CodeEntry::GetCallUid() const {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000194 uint32_t hash = ComputeIntegerHash(tag_, v8::internal::kZeroHashSeed);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000195 if (shared_id_ != 0) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000196 hash ^= ComputeIntegerHash(static_cast<uint32_t>(shared_id_),
197 v8::internal::kZeroHashSeed);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000198 } else {
199 hash ^= ComputeIntegerHash(
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000200 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_prefix_)),
201 v8::internal::kZeroHashSeed);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000202 hash ^= ComputeIntegerHash(
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000203 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name_)),
204 v8::internal::kZeroHashSeed);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000205 hash ^= ComputeIntegerHash(
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000206 static_cast<uint32_t>(reinterpret_cast<uintptr_t>(resource_name_)),
207 v8::internal::kZeroHashSeed);
208 hash ^= ComputeIntegerHash(line_number_, v8::internal::kZeroHashSeed);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000209 }
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000210 return hash;
211}
212
213
214bool CodeEntry::IsSameAs(CodeEntry* entry) const {
215 return this == entry
216 || (tag_ == entry->tag_
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000217 && shared_id_ == entry->shared_id_
218 && (shared_id_ != 0
219 || (name_prefix_ == entry->name_prefix_
220 && name_ == entry->name_
221 && resource_name_ == entry->resource_name_
222 && line_number_ == entry->line_number_)));
fschneider@chromium.orgc20610a2010-09-22 09:44:58 +0000223}
224
225
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000226ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
227 HashMap::Entry* map_entry =
228 children_.Lookup(entry, CodeEntryHash(entry), false);
229 return map_entry != NULL ?
230 reinterpret_cast<ProfileNode*>(map_entry->value) : NULL;
231}
232
233
234ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
235 HashMap::Entry* map_entry =
236 children_.Lookup(entry, CodeEntryHash(entry), true);
237 if (map_entry->value == NULL) {
238 // New node added.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000239 ProfileNode* new_node = new ProfileNode(tree_, entry);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000240 map_entry->value = new_node;
241 children_list_.Add(new_node);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000242 }
243 return reinterpret_cast<ProfileNode*>(map_entry->value);
244}
245
246
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000247double ProfileNode::GetSelfMillis() const {
248 return tree_->TicksToMillis(self_ticks_);
249}
250
251
252double ProfileNode::GetTotalMillis() const {
253 return tree_->TicksToMillis(total_ticks_);
254}
255
256
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000257void ProfileNode::Print(int indent) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000258 OS::Print("%5u %5u %*c %s%s [%d]",
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000259 total_ticks_, self_ticks_,
260 indent, ' ',
ager@chromium.org357bf652010-04-12 11:30:10 +0000261 entry_->name_prefix(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000262 entry_->name(),
263 entry_->security_token_id());
ager@chromium.org357bf652010-04-12 11:30:10 +0000264 if (entry_->resource_name()[0] != '\0')
265 OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
266 OS::Print("\n");
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000267 for (HashMap::Entry* p = children_.Start();
268 p != NULL;
269 p = children_.Next(p)) {
270 reinterpret_cast<ProfileNode*>(p->value)->Print(indent + 2);
271 }
272}
273
274
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000275class DeleteNodesCallback {
276 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000277 void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
278
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000279 void AfterAllChildrenTraversed(ProfileNode* node) {
280 delete node;
281 }
282
283 void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
284};
285
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000286
ager@chromium.org357bf652010-04-12 11:30:10 +0000287ProfileTree::ProfileTree()
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000288 : root_entry_(Logger::FUNCTION_TAG,
289 "",
290 "(root)",
291 "",
292 0,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000293 TokenEnumerator::kNoSecurityToken),
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000294 root_(new ProfileNode(this, &root_entry_)) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000295}
296
297
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000298ProfileTree::~ProfileTree() {
299 DeleteNodesCallback cb;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000300 TraverseDepthFirst(&cb);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000301}
302
303
304void ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path) {
305 ProfileNode* node = root_;
306 for (CodeEntry** entry = path.start() + path.length() - 1;
307 entry != path.start() - 1;
308 --entry) {
309 if (*entry != NULL) {
310 node = node->FindOrAddChild(*entry);
311 }
312 }
313 node->IncrementSelfTicks();
314}
315
316
317void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) {
318 ProfileNode* node = root_;
319 for (CodeEntry** entry = path.start();
320 entry != path.start() + path.length();
321 ++entry) {
322 if (*entry != NULL) {
323 node = node->FindOrAddChild(*entry);
324 }
325 }
326 node->IncrementSelfTicks();
327}
328
329
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000330struct NodesPair {
331 NodesPair(ProfileNode* src, ProfileNode* dst)
332 : src(src), dst(dst) { }
333 ProfileNode* src;
334 ProfileNode* dst;
335};
336
337
338class FilteredCloneCallback {
339 public:
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000340 FilteredCloneCallback(ProfileNode* dst_root, int security_token_id)
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000341 : stack_(10),
342 security_token_id_(security_token_id) {
343 stack_.Add(NodesPair(NULL, dst_root));
344 }
345
346 void BeforeTraversingChild(ProfileNode* parent, ProfileNode* child) {
347 if (IsTokenAcceptable(child->entry()->security_token_id(),
348 parent->entry()->security_token_id())) {
349 ProfileNode* clone = stack_.last().dst->FindOrAddChild(child->entry());
350 clone->IncreaseSelfTicks(child->self_ticks());
351 stack_.Add(NodesPair(child, clone));
352 } else {
353 // Attribute ticks to parent node.
354 stack_.last().dst->IncreaseSelfTicks(child->self_ticks());
355 }
356 }
357
358 void AfterAllChildrenTraversed(ProfileNode* parent) { }
359
360 void AfterChildTraversed(ProfileNode*, ProfileNode* child) {
361 if (stack_.last().src == child) {
362 stack_.RemoveLast();
363 }
364 }
365
366 private:
367 bool IsTokenAcceptable(int token, int parent_token) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000368 if (token == TokenEnumerator::kNoSecurityToken
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000369 || token == security_token_id_) return true;
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000370 if (token == TokenEnumerator::kInheritsSecurityToken) {
371 ASSERT(parent_token != TokenEnumerator::kInheritsSecurityToken);
372 return parent_token == TokenEnumerator::kNoSecurityToken
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000373 || parent_token == security_token_id_;
374 }
375 return false;
376 }
377
378 List<NodesPair> stack_;
379 int security_token_id_;
380};
381
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000382void ProfileTree::FilteredClone(ProfileTree* src, int security_token_id) {
383 ms_to_ticks_scale_ = src->ms_to_ticks_scale_;
384 FilteredCloneCallback cb(root_, security_token_id);
385 src->TraverseDepthFirst(&cb);
386 CalculateTotalTicks();
387}
388
389
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000390void ProfileTree::SetTickRatePerMs(double ticks_per_ms) {
391 ms_to_ticks_scale_ = ticks_per_ms > 0 ? 1.0 / ticks_per_ms : 1.0;
392}
393
394
lrn@chromium.org25156de2010-04-06 13:10:27 +0000395class Position {
396 public:
397 explicit Position(ProfileNode* node)
398 : node(node), child_idx_(0) { }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000399 INLINE(ProfileNode* current_child()) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000400 return node->children()->at(child_idx_);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000401 }
lrn@chromium.org25156de2010-04-06 13:10:27 +0000402 INLINE(bool has_current_child()) {
403 return child_idx_ < node->children()->length();
404 }
405 INLINE(void next_child()) { ++child_idx_; }
406
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000407 ProfileNode* node;
lrn@chromium.org25156de2010-04-06 13:10:27 +0000408 private:
409 int child_idx_;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000410};
411
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000412
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000413// Non-recursive implementation of a depth-first post-order tree traversal.
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000414template <typename Callback>
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000415void ProfileTree::TraverseDepthFirst(Callback* callback) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000416 List<Position> stack(10);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000417 stack.Add(Position(root_));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000418 while (stack.length() > 0) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000419 Position& current = stack.last();
lrn@chromium.org25156de2010-04-06 13:10:27 +0000420 if (current.has_current_child()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000421 callback->BeforeTraversingChild(current.node, current.current_child());
lrn@chromium.org25156de2010-04-06 13:10:27 +0000422 stack.Add(Position(current.current_child()));
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000423 } else {
424 callback->AfterAllChildrenTraversed(current.node);
425 if (stack.length() > 1) {
426 Position& parent = stack[stack.length() - 2];
427 callback->AfterChildTraversed(parent.node, current.node);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000428 parent.next_child();
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000429 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000430 // Remove child from the stack.
431 stack.RemoveLast();
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000432 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000433 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000434}
435
436
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000437class CalculateTotalTicksCallback {
438 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000439 void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
440
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000441 void AfterAllChildrenTraversed(ProfileNode* node) {
442 node->IncreaseTotalTicks(node->self_ticks());
443 }
444
445 void AfterChildTraversed(ProfileNode* parent, ProfileNode* child) {
446 parent->IncreaseTotalTicks(child->total_ticks());
447 }
448};
449
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000450
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000451void ProfileTree::CalculateTotalTicks() {
452 CalculateTotalTicksCallback cb;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000453 TraverseDepthFirst(&cb);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000454}
455
456
457void ProfileTree::ShortPrint() {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000458 OS::Print("root: %u %u %.2fms %.2fms\n",
459 root_->total_ticks(), root_->self_ticks(),
460 root_->GetTotalMillis(), root_->GetSelfMillis());
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000461}
462
463
464void CpuProfile::AddPath(const Vector<CodeEntry*>& path) {
465 top_down_.AddPathFromEnd(path);
466 bottom_up_.AddPathFromStart(path);
467}
468
469
470void CpuProfile::CalculateTotalTicks() {
471 top_down_.CalculateTotalTicks();
472 bottom_up_.CalculateTotalTicks();
473}
474
475
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000476void CpuProfile::SetActualSamplingRate(double actual_sampling_rate) {
477 top_down_.SetTickRatePerMs(actual_sampling_rate);
478 bottom_up_.SetTickRatePerMs(actual_sampling_rate);
479}
480
481
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000482CpuProfile* CpuProfile::FilteredClone(int security_token_id) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000483 ASSERT(security_token_id != TokenEnumerator::kNoSecurityToken);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000484 CpuProfile* clone = new CpuProfile(title_, uid_);
485 clone->top_down_.FilteredClone(&top_down_, security_token_id);
486 clone->bottom_up_.FilteredClone(&bottom_up_, security_token_id);
487 return clone;
488}
489
490
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000491void CpuProfile::ShortPrint() {
492 OS::Print("top down ");
493 top_down_.ShortPrint();
494 OS::Print("bottom up ");
495 bottom_up_.ShortPrint();
496}
497
498
499void CpuProfile::Print() {
500 OS::Print("[Top down]:\n");
501 top_down_.Print();
502 OS::Print("[Bottom up]:\n");
503 bottom_up_.Print();
504}
505
506
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000507CodeEntry* const CodeMap::kSharedFunctionCodeEntry = NULL;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000508const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000509
510
lrn@chromium.org34e60782011-09-15 07:25:40 +0000511void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
512 DeleteAllCoveredCode(addr, addr + size);
513 CodeTree::Locator locator;
514 tree_.Insert(addr, &locator);
515 locator.set_value(CodeEntryInfo(entry, size));
516}
517
518
519void CodeMap::DeleteAllCoveredCode(Address start, Address end) {
520 List<Address> to_delete;
521 Address addr = end - 1;
522 while (addr >= start) {
523 CodeTree::Locator locator;
524 if (!tree_.FindGreatestLessThan(addr, &locator)) break;
525 Address start2 = locator.key(), end2 = start2 + locator.value().size;
526 if (start2 < end && start < end2) to_delete.Add(start2);
527 addr = start2 - 1;
528 }
529 for (int i = 0; i < to_delete.length(); ++i) tree_.Remove(to_delete[i]);
530}
531
532
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000533CodeEntry* CodeMap::FindEntry(Address addr) {
534 CodeTree::Locator locator;
535 if (tree_.FindGreatestLessThan(addr, &locator)) {
536 // locator.key() <= addr. Need to check that addr is within entry.
537 const CodeEntryInfo& entry = locator.value();
538 if (addr < (locator.key() + entry.size))
539 return entry.entry;
540 }
541 return NULL;
542}
543
544
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000545int CodeMap::GetSharedId(Address addr) {
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000546 CodeTree::Locator locator;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000547 // For shared function entries, 'size' field is used to store their IDs.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000548 if (tree_.Find(addr, &locator)) {
549 const CodeEntryInfo& entry = locator.value();
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000550 ASSERT(entry.entry == kSharedFunctionCodeEntry);
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000551 return entry.size;
552 } else {
553 tree_.Insert(addr, &locator);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +0000554 int id = next_shared_id_++;
555 locator.set_value(CodeEntryInfo(kSharedFunctionCodeEntry, id));
556 return id;
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000557 }
558}
559
560
lrn@chromium.org34e60782011-09-15 07:25:40 +0000561void CodeMap::MoveCode(Address from, Address to) {
562 if (from == to) return;
563 CodeTree::Locator locator;
564 if (!tree_.Find(from, &locator)) return;
565 CodeEntryInfo entry = locator.value();
566 tree_.Remove(from);
567 AddCode(to, entry.entry, entry.size);
568}
569
570
lrn@chromium.org25156de2010-04-06 13:10:27 +0000571void CodeMap::CodeTreePrinter::Call(
572 const Address& key, const CodeMap::CodeEntryInfo& value) {
573 OS::Print("%p %5d %s\n", key, value.size, value.entry->name());
574}
575
576
577void CodeMap::Print() {
578 CodeTreePrinter printer;
579 tree_.ForEach(&printer);
580}
581
582
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000583CpuProfilesCollection::CpuProfilesCollection()
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000584 : profiles_uids_(UidsMatch),
lrn@chromium.org25156de2010-04-06 13:10:27 +0000585 current_profiles_semaphore_(OS::CreateSemaphore(1)) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000586 // Create list of unabridged profiles.
587 profiles_by_token_.Add(new List<CpuProfile*>());
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000588}
589
590
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000591static void DeleteCodeEntry(CodeEntry** entry_ptr) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000592 delete *entry_ptr;
593}
594
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000595static void DeleteCpuProfile(CpuProfile** profile_ptr) {
596 delete *profile_ptr;
597}
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000598
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000599static void DeleteProfilesList(List<CpuProfile*>** list_ptr) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000600 if (*list_ptr != NULL) {
601 (*list_ptr)->Iterate(DeleteCpuProfile);
602 delete *list_ptr;
603 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000604}
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000605
606CpuProfilesCollection::~CpuProfilesCollection() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000607 delete current_profiles_semaphore_;
608 current_profiles_.Iterate(DeleteCpuProfile);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000609 detached_profiles_.Iterate(DeleteCpuProfile);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000610 profiles_by_token_.Iterate(DeleteProfilesList);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000611 code_entries_.Iterate(DeleteCodeEntry);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000612}
613
614
lrn@chromium.org25156de2010-04-06 13:10:27 +0000615bool CpuProfilesCollection::StartProfiling(const char* title, unsigned uid) {
616 ASSERT(uid > 0);
617 current_profiles_semaphore_->Wait();
ricow@chromium.orgd236f4d2010-09-01 06:52:08 +0000618 if (current_profiles_.length() >= kMaxSimultaneousProfiles) {
619 current_profiles_semaphore_->Signal();
620 return false;
621 }
lrn@chromium.org25156de2010-04-06 13:10:27 +0000622 for (int i = 0; i < current_profiles_.length(); ++i) {
623 if (strcmp(current_profiles_[i]->title(), title) == 0) {
624 // Ignore attempts to start profile with the same title.
625 current_profiles_semaphore_->Signal();
626 return false;
627 }
628 }
629 current_profiles_.Add(new CpuProfile(title, uid));
630 current_profiles_semaphore_->Signal();
631 return true;
632}
633
634
635bool CpuProfilesCollection::StartProfiling(String* title, unsigned uid) {
636 return StartProfiling(GetName(title), uid);
637}
638
639
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000640CpuProfile* CpuProfilesCollection::StopProfiling(int security_token_id,
641 const char* title,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000642 double actual_sampling_rate) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000643 const int title_len = StrLength(title);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000644 CpuProfile* profile = NULL;
645 current_profiles_semaphore_->Wait();
646 for (int i = current_profiles_.length() - 1; i >= 0; --i) {
647 if (title_len == 0 || strcmp(current_profiles_[i]->title(), title) == 0) {
648 profile = current_profiles_.Remove(i);
649 break;
650 }
651 }
652 current_profiles_semaphore_->Signal();
653
654 if (profile != NULL) {
655 profile->CalculateTotalTicks();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000656 profile->SetActualSamplingRate(actual_sampling_rate);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000657 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000658 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000659 unabridged_list->Add(profile);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000660 HashMap::Entry* entry =
661 profiles_uids_.Lookup(reinterpret_cast<void*>(profile->uid()),
662 static_cast<uint32_t>(profile->uid()),
663 true);
664 ASSERT(entry->value == NULL);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000665 entry->value = reinterpret_cast<void*>(unabridged_list->length() - 1);
666 return GetProfile(security_token_id, profile->uid());
lrn@chromium.org25156de2010-04-06 13:10:27 +0000667 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000668 return NULL;
lrn@chromium.org25156de2010-04-06 13:10:27 +0000669}
670
671
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000672CpuProfile* CpuProfilesCollection::GetProfile(int security_token_id,
673 unsigned uid) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000674 int index = GetProfileIndex(uid);
675 if (index < 0) return NULL;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000676 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000677 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
678 if (security_token_id == TokenEnumerator::kNoSecurityToken) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000679 return unabridged_list->at(index);
680 }
681 List<CpuProfile*>* list = GetProfilesList(security_token_id);
682 if (list->at(index) == NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000683 (*list)[index] =
684 unabridged_list->at(index)->FilteredClone(security_token_id);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000685 }
686 return list->at(index);
687}
688
689
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000690int CpuProfilesCollection::GetProfileIndex(unsigned uid) {
691 HashMap::Entry* entry = profiles_uids_.Lookup(reinterpret_cast<void*>(uid),
692 static_cast<uint32_t>(uid),
693 false);
694 return entry != NULL ?
695 static_cast<int>(reinterpret_cast<intptr_t>(entry->value)) : -1;
696}
697
698
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000699bool CpuProfilesCollection::IsLastProfile(const char* title) {
700 // Called from VM thread, and only it can mutate the list,
701 // so no locking is needed here.
702 if (current_profiles_.length() != 1) return false;
703 return StrLength(title) == 0
704 || strcmp(current_profiles_[0]->title(), title) == 0;
705}
706
707
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000708void CpuProfilesCollection::RemoveProfile(CpuProfile* profile) {
709 // Called from VM thread for a completed profile.
710 unsigned uid = profile->uid();
711 int index = GetProfileIndex(uid);
712 if (index < 0) {
713 detached_profiles_.RemoveElement(profile);
714 return;
715 }
716 profiles_uids_.Remove(reinterpret_cast<void*>(uid),
717 static_cast<uint32_t>(uid));
718 // Decrement all indexes above the deleted one.
719 for (HashMap::Entry* p = profiles_uids_.Start();
720 p != NULL;
721 p = profiles_uids_.Next(p)) {
722 intptr_t p_index = reinterpret_cast<intptr_t>(p->value);
723 if (p_index > index) {
724 p->value = reinterpret_cast<void*>(p_index - 1);
725 }
726 }
727 for (int i = 0; i < profiles_by_token_.length(); ++i) {
728 List<CpuProfile*>* list = profiles_by_token_[i];
729 if (list != NULL && index < list->length()) {
730 // Move all filtered clones into detached_profiles_,
731 // so we can know that they are still in use.
732 CpuProfile* cloned_profile = list->Remove(index);
733 if (cloned_profile != NULL && cloned_profile != profile) {
734 detached_profiles_.Add(cloned_profile);
735 }
736 }
737 }
738}
739
740
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000741int CpuProfilesCollection::TokenToIndex(int security_token_id) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000742 ASSERT(TokenEnumerator::kNoSecurityToken == -1);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000743 return security_token_id + 1; // kNoSecurityToken -> 0, 0 -> 1, ...
744}
745
746
747List<CpuProfile*>* CpuProfilesCollection::GetProfilesList(
748 int security_token_id) {
749 const int index = TokenToIndex(security_token_id);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000750 const int lists_to_add = index - profiles_by_token_.length() + 1;
751 if (lists_to_add > 0) profiles_by_token_.AddBlock(NULL, lists_to_add);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000752 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000753 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000754 const int current_count = unabridged_list->length();
755 if (profiles_by_token_[index] == NULL) {
756 profiles_by_token_[index] = new List<CpuProfile*>(current_count);
757 }
758 List<CpuProfile*>* list = profiles_by_token_[index];
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000759 const int profiles_to_add = current_count - list->length();
760 if (profiles_to_add > 0) list->AddBlock(NULL, profiles_to_add);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000761 return list;
762}
763
764
765List<CpuProfile*>* CpuProfilesCollection::Profiles(int security_token_id) {
766 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000767 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
768 if (security_token_id == TokenEnumerator::kNoSecurityToken) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000769 return unabridged_list;
770 }
771 List<CpuProfile*>* list = GetProfilesList(security_token_id);
772 const int current_count = unabridged_list->length();
773 for (int i = 0; i < current_count; ++i) {
774 if (list->at(i) == NULL) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +0000775 (*list)[i] = unabridged_list->at(i)->FilteredClone(security_token_id);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000776 }
777 }
778 return list;
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000779}
780
781
782CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
783 String* name,
784 String* resource_name,
785 int line_number) {
786 CodeEntry* entry = new CodeEntry(tag,
lrn@chromium.org25156de2010-04-06 13:10:27 +0000787 CodeEntry::kEmptyNamePrefix,
ager@chromium.org357bf652010-04-12 11:30:10 +0000788 GetFunctionName(name),
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000789 GetName(resource_name),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000790 line_number,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000791 TokenEnumerator::kNoSecurityToken);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000792 code_entries_.Add(entry);
793 return entry;
794}
795
796
797CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
798 const char* name) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000799 CodeEntry* entry = new CodeEntry(tag,
800 CodeEntry::kEmptyNamePrefix,
ager@chromium.org357bf652010-04-12 11:30:10 +0000801 GetFunctionName(name),
lrn@chromium.org25156de2010-04-06 13:10:27 +0000802 "",
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000803 v8::CpuProfileNode::kNoLineNumberInfo,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000804 TokenEnumerator::kNoSecurityToken);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000805 code_entries_.Add(entry);
806 return entry;
807}
808
809
810CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
811 const char* name_prefix,
812 String* name) {
813 CodeEntry* entry = new CodeEntry(tag,
814 name_prefix,
815 GetName(name),
816 "",
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000817 v8::CpuProfileNode::kNoLineNumberInfo,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000818 TokenEnumerator::kInheritsSecurityToken);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000819 code_entries_.Add(entry);
820 return entry;
821}
822
823
824CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
825 int args_count) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000826 CodeEntry* entry = new CodeEntry(tag,
827 "args_count: ",
828 GetName(args_count),
829 "",
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000830 v8::CpuProfileNode::kNoLineNumberInfo,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000831 TokenEnumerator::kInheritsSecurityToken);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000832 code_entries_.Add(entry);
833 return entry;
834}
835
836
lrn@chromium.org25156de2010-04-06 13:10:27 +0000837void CpuProfilesCollection::AddPathToCurrentProfiles(
838 const Vector<CodeEntry*>& path) {
839 // As starting / stopping profiles is rare relatively to this
840 // method, we don't bother minimizing the duration of lock holding,
841 // e.g. copying contents of the list to a local vector.
842 current_profiles_semaphore_->Wait();
843 for (int i = 0; i < current_profiles_.length(); ++i) {
844 current_profiles_[i]->AddPath(path);
845 }
846 current_profiles_semaphore_->Signal();
847}
848
849
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000850void SampleRateCalculator::Tick() {
851 if (--wall_time_query_countdown_ == 0)
852 UpdateMeasurements(OS::TimeCurrentMillis());
853}
854
855
856void SampleRateCalculator::UpdateMeasurements(double current_time) {
857 if (measurements_count_++ != 0) {
858 const double measured_ticks_per_ms =
859 (kWallTimeQueryIntervalMs * ticks_per_ms_) /
860 (current_time - last_wall_time_);
861 // Update the average value.
862 ticks_per_ms_ +=
863 (measured_ticks_per_ms - ticks_per_ms_) / measurements_count_;
864 // Update the externally accessible result.
865 result_ = static_cast<AtomicWord>(ticks_per_ms_ * kResultScale);
866 }
867 last_wall_time_ = current_time;
868 wall_time_query_countdown_ =
869 static_cast<unsigned>(kWallTimeQueryIntervalMs * ticks_per_ms_);
870}
871
872
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000873const char* const ProfileGenerator::kAnonymousFunctionName =
874 "(anonymous function)";
875const char* const ProfileGenerator::kProgramEntryName =
876 "(program)";
877const char* const ProfileGenerator::kGarbageCollectorEntryName =
878 "(garbage collector)";
ager@chromium.org357bf652010-04-12 11:30:10 +0000879
880
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000881ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
ager@chromium.org357bf652010-04-12 11:30:10 +0000882 : profiles_(profiles),
883 program_entry_(
884 profiles->NewCodeEntry(Logger::FUNCTION_TAG, kProgramEntryName)),
885 gc_entry_(
886 profiles->NewCodeEntry(Logger::BUILTIN_TAG,
887 kGarbageCollectorEntryName)) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000888}
889
890
891void ProfileGenerator::RecordTickSample(const TickSample& sample) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000892 // Allocate space for stack frames + pc + function + vm-state.
893 ScopedVector<CodeEntry*> entries(sample.frames_count + 3);
894 // As actual number of decoded code entries may vary, initialize
895 // entries vector with NULL values.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000896 CodeEntry** entry = entries.start();
ager@chromium.org357bf652010-04-12 11:30:10 +0000897 memset(entry, 0, entries.length() * sizeof(*entry));
898 if (sample.pc != NULL) {
899 *entry++ = code_map_.FindEntry(sample.pc);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000900
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +0000901 if (sample.has_external_callback) {
902 // Don't use PC when in external callback code, as it can point
903 // inside callback's code, and we will erroneously report
904 // that a callback calls itself.
905 *(entries.start()) = NULL;
906 *entry++ = code_map_.FindEntry(sample.external_callback);
907 } else if (sample.tos != NULL) {
908 // Find out, if top of stack was pointing inside a JS function
909 // meaning that we have encountered a frameless invocation.
fschneider@chromium.org3a5fd782011-02-24 10:10:44 +0000910 *entry = code_map_.FindEntry(sample.tos);
ager@chromium.org357bf652010-04-12 11:30:10 +0000911 if (*entry != NULL && !(*entry)->is_js_function()) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000912 *entry = NULL;
ager@chromium.org357bf652010-04-12 11:30:10 +0000913 }
914 entry++;
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000915 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000916
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +0000917 for (const Address* stack_pos = sample.stack,
ager@chromium.org357bf652010-04-12 11:30:10 +0000918 *stack_end = stack_pos + sample.frames_count;
919 stack_pos != stack_end;
920 ++stack_pos) {
921 *entry++ = code_map_.FindEntry(*stack_pos);
922 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000923 }
924
ager@chromium.org357bf652010-04-12 11:30:10 +0000925 if (FLAG_prof_browser_mode) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000926 bool no_symbolized_entries = true;
927 for (CodeEntry** e = entries.start(); e != entry; ++e) {
928 if (*e != NULL) {
929 no_symbolized_entries = false;
930 break;
931 }
932 }
933 // If no frames were symbolized, put the VM state entry in.
934 if (no_symbolized_entries) {
935 *entry++ = EntryForVMState(sample.state);
936 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000937 }
938
lrn@chromium.org25156de2010-04-06 13:10:27 +0000939 profiles_->AddPathToCurrentProfiles(entries);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000940}
941
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000942
jkummerow@chromium.org212d9642012-05-11 15:02:09 +0000943HeapGraphEdge::HeapGraphEdge(Type type, const char* name, int from, int to)
944 : type_(type),
945 from_index_(from),
946 to_index_(to),
947 name_(name) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000948 ASSERT(type == kContextVariable
jkummerow@chromium.org212d9642012-05-11 15:02:09 +0000949 || type == kProperty
950 || type == kInternal
951 || type == kShortcut);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000952}
953
954
jkummerow@chromium.org212d9642012-05-11 15:02:09 +0000955HeapGraphEdge::HeapGraphEdge(Type type, int index, int from, int to)
956 : type_(type),
957 from_index_(from),
958 to_index_(to),
959 index_(index) {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +0000960 ASSERT(type == kElement || type == kHidden || type == kWeak);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000961}
962
963
jkummerow@chromium.org212d9642012-05-11 15:02:09 +0000964void HeapGraphEdge::ReplaceToIndexWithEntry(HeapSnapshot* snapshot) {
965 to_entry_ = &snapshot->entries()[to_index_];
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000966}
967
968
jkummerow@chromium.org212d9642012-05-11 15:02:09 +0000969const int HeapEntry::kNoEntry = -1;
970
971HeapEntry::HeapEntry(HeapSnapshot* snapshot,
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000972 Type type,
973 const char* name,
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +0000974 SnapshotObjectId id,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +0000975 int self_size)
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +0000976 : type_(type),
jkummerow@chromium.org212d9642012-05-11 15:02:09 +0000977 children_count_(0),
978 children_index_(-1),
979 self_size_(self_size),
jkummerow@chromium.org212d9642012-05-11 15:02:09 +0000980 id_(id),
981 snapshot_(snapshot),
982 name_(name) { }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000983
984
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000985void HeapEntry::SetNamedReference(HeapGraphEdge::Type type,
vegorov@chromium.org26c16f82010-08-11 13:41:03 +0000986 const char* name,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +0000987 HeapEntry* entry) {
988 HeapGraphEdge edge(type, name, this->index(), entry->index());
989 snapshot_->edges().Add(edge);
990 ++children_count_;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000991}
992
993
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000994void HeapEntry::SetIndexedReference(HeapGraphEdge::Type type,
vegorov@chromium.org21b5e952010-11-23 10:24:40 +0000995 int index,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +0000996 HeapEntry* entry) {
997 HeapGraphEdge edge(type, index, this->index(), entry->index());
998 snapshot_->edges().Add(edge);
999 ++children_count_;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00001000}
1001
1002
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001003Handle<HeapObject> HeapEntry::GetHeapObject() {
1004 return snapshot_->collection()->FindHeapObjectById(id());
1005}
1006
1007
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001008void HeapEntry::Print(
1009 const char* prefix, const char* edge_name, int max_depth, int indent) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001010 STATIC_CHECK(sizeof(unsigned) == sizeof(id()));
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00001011 OS::Print("%6d @%6u %*c %s%s: ",
1012 self_size(), id(), indent, ' ', prefix, edge_name);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001013 if (type() != kString) {
1014 OS::Print("%s %.40s\n", TypeAsString(), name_);
1015 } else {
1016 OS::Print("\"");
1017 const char* c = name_;
1018 while (*c && (c - name_) <= 40) {
1019 if (*c != '\n')
1020 OS::Print("%c", *c);
1021 else
1022 OS::Print("\\n");
1023 ++c;
1024 }
1025 OS::Print("\"\n");
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001026 }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001027 if (--max_depth == 0) return;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001028 Vector<HeapGraphEdge*> ch = children();
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001029 for (int i = 0; i < ch.length(); ++i) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001030 HeapGraphEdge& edge = *ch[i];
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001031 const char* edge_prefix = "";
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00001032 EmbeddedVector<char, 64> index;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001033 const char* edge_name = index.start();
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001034 switch (edge.type()) {
1035 case HeapGraphEdge::kContextVariable:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001036 edge_prefix = "#";
1037 edge_name = edge.name();
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001038 break;
1039 case HeapGraphEdge::kElement:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001040 OS::SNPrintF(index, "%d", edge.index());
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001041 break;
1042 case HeapGraphEdge::kInternal:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001043 edge_prefix = "$";
1044 edge_name = edge.name();
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001045 break;
1046 case HeapGraphEdge::kProperty:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001047 edge_name = edge.name();
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001048 break;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001049 case HeapGraphEdge::kHidden:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001050 edge_prefix = "$";
1051 OS::SNPrintF(index, "%d", edge.index());
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001052 break;
1053 case HeapGraphEdge::kShortcut:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001054 edge_prefix = "^";
1055 edge_name = edge.name();
1056 break;
1057 case HeapGraphEdge::kWeak:
1058 edge_prefix = "w";
1059 OS::SNPrintF(index, "%d", edge.index());
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001060 break;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001061 default:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001062 OS::SNPrintF(index, "!!! unknown edge type: %d ", edge.type());
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001063 }
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001064 edge.to()->Print(edge_prefix, edge_name, max_depth, indent + 2);
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001065 }
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001066}
1067
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001068
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001069const char* HeapEntry::TypeAsString() {
1070 switch (type()) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001071 case kHidden: return "/hidden/";
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001072 case kObject: return "/object/";
1073 case kClosure: return "/closure/";
1074 case kString: return "/string/";
1075 case kCode: return "/code/";
1076 case kArray: return "/array/";
vegorov@chromium.org42841962010-10-18 11:18:59 +00001077 case kRegExp: return "/regexp/";
1078 case kHeapNumber: return "/number/";
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001079 case kNative: return "/native/";
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00001080 case kSynthetic: return "/synthetic/";
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001081 default: return "???";
1082 }
1083}
1084
1085
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001086// It is very important to keep objects that form a heap snapshot
1087// as small as possible.
1088namespace { // Avoid littering the global namespace.
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001089
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001090template <size_t ptr_size> struct SnapshotSizeConstants;
1091
1092template <> struct SnapshotSizeConstants<4> {
1093 static const int kExpectedHeapGraphEdgeSize = 12;
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00001094 static const int kExpectedHeapEntrySize = 24;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001095 static const int kExpectedHeapSnapshotsCollectionSize = 96;
1096 static const int kExpectedHeapSnapshotSize = 136;
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00001097 static const size_t kMaxSerializableSnapshotRawSize = 256 * MB;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001098};
1099
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001100template <> struct SnapshotSizeConstants<8> {
1101 static const int kExpectedHeapGraphEdgeSize = 24;
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00001102 static const int kExpectedHeapEntrySize = 32;
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001103 static const int kExpectedHeapSnapshotsCollectionSize = 144;
1104 static const int kExpectedHeapSnapshotSize = 168;
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00001105 static const uint64_t kMaxSerializableSnapshotRawSize =
1106 static_cast<uint64_t>(6000) * MB;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001107};
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001108
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001109} // namespace
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001110
1111HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001112 HeapSnapshot::Type type,
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001113 const char* title,
1114 unsigned uid)
1115 : collection_(collection),
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001116 type_(type),
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001117 title_(title),
1118 uid_(uid),
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001119 root_index_(HeapEntry::kNoEntry),
1120 gc_roots_index_(HeapEntry::kNoEntry),
1121 natives_root_index_(HeapEntry::kNoEntry),
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00001122 max_snapshot_js_object_id_(0) {
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00001123 STATIC_CHECK(
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001124 sizeof(HeapGraphEdge) ==
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001125 SnapshotSizeConstants<kPointerSize>::kExpectedHeapGraphEdgeSize);
mstarzinger@chromium.org3233d2f2012-03-14 11:16:03 +00001126 STATIC_CHECK(
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001127 sizeof(HeapEntry) ==
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001128 SnapshotSizeConstants<kPointerSize>::kExpectedHeapEntrySize);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001129 for (int i = 0; i < VisitorSynchronization::kNumberOfSyncTags; ++i) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001130 gc_subroot_indexes_[i] = HeapEntry::kNoEntry;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001131 }
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001132}
1133
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00001134
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001135void HeapSnapshot::Delete() {
1136 collection_->RemoveSnapshot(this);
1137 delete this;
1138}
1139
1140
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00001141void HeapSnapshot::RememberLastJSObjectId() {
1142 max_snapshot_js_object_id_ = collection_->last_assigned_id();
1143}
1144
1145
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001146HeapEntry* HeapSnapshot::AddRootEntry() {
1147 ASSERT(root_index_ == HeapEntry::kNoEntry);
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00001148 ASSERT(entries_.is_empty()); // Root entry must be the first one.
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001149 HeapEntry* entry = AddEntry(HeapEntry::kObject,
1150 "",
1151 HeapObjectsMap::kInternalRootObjectId,
1152 0);
1153 root_index_ = entry->index();
1154 ASSERT(root_index_ == 0);
1155 return entry;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001156}
1157
1158
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001159HeapEntry* HeapSnapshot::AddGcRootsEntry() {
1160 ASSERT(gc_roots_index_ == HeapEntry::kNoEntry);
1161 HeapEntry* entry = AddEntry(HeapEntry::kObject,
1162 "(GC roots)",
1163 HeapObjectsMap::kGcRootsObjectId,
1164 0);
1165 gc_roots_index_ = entry->index();
1166 return entry;
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001167}
1168
1169
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001170HeapEntry* HeapSnapshot::AddGcSubrootEntry(int tag) {
1171 ASSERT(gc_subroot_indexes_[tag] == HeapEntry::kNoEntry);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001172 ASSERT(0 <= tag && tag < VisitorSynchronization::kNumberOfSyncTags);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001173 HeapEntry* entry = AddEntry(
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001174 HeapEntry::kObject,
1175 VisitorSynchronization::kTagNames[tag],
1176 HeapObjectsMap::GetNthGcSubrootId(tag),
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001177 0);
1178 gc_subroot_indexes_[tag] = entry->index();
1179 return entry;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001180}
1181
1182
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001183HeapEntry* HeapSnapshot::AddEntry(HeapEntry::Type type,
1184 const char* name,
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00001185 SnapshotObjectId id,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001186 int size) {
1187 HeapEntry entry(this, type, name, id, size);
1188 entries_.Add(entry);
1189 return &entries_.last();
1190}
1191
1192
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00001193void HeapSnapshot::FillChildren() {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001194 ASSERT(children().is_empty());
1195 children().Allocate(edges().length());
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001196 int children_index = 0;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001197 for (int i = 0; i < entries().length(); ++i) {
1198 HeapEntry* entry = &entries()[i];
1199 children_index = entry->set_children_index(children_index);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001200 }
1201 ASSERT(edges().length() == children_index);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001202 for (int i = 0; i < edges().length(); ++i) {
1203 HeapGraphEdge* edge = &edges()[i];
1204 edge->ReplaceToIndexWithEntry(this);
1205 edge->from()->add_child(edge);
ager@chromium.orgbeb25712010-11-29 08:02:25 +00001206 }
1207}
1208
1209
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001210class FindEntryById {
1211 public:
1212 explicit FindEntryById(SnapshotObjectId id) : id_(id) { }
1213 int operator()(HeapEntry* const* entry) {
1214 if ((*entry)->id() == id_) return 0;
1215 return (*entry)->id() < id_ ? -1 : 1;
1216 }
1217 private:
1218 SnapshotObjectId id_;
1219};
1220
1221
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00001222HeapEntry* HeapSnapshot::GetEntryById(SnapshotObjectId id) {
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001223 List<HeapEntry*>* entries_by_id = GetSortedEntriesList();
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001224 // Perform a binary search by id.
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001225 int index = SortedListBSearch(*entries_by_id, FindEntryById(id));
1226 if (index == -1)
1227 return NULL;
1228 return entries_by_id->at(index);
kasperl@chromium.orga5551262010-12-07 12:49:48 +00001229}
1230
1231
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001232template<class T>
1233static int SortByIds(const T* entry1_ptr,
1234 const T* entry2_ptr) {
1235 if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0;
1236 return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1;
1237}
1238
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00001239
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001240List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00001241 if (sorted_entries_.is_empty()) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001242 sorted_entries_.Allocate(entries_.length());
1243 for (int i = 0; i < entries_.length(); ++i) {
1244 sorted_entries_[i] = &entries_[i];
1245 }
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00001246 sorted_entries_.Sort(SortByIds);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001247 }
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00001248 return &sorted_entries_;
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001249}
1250
1251
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001252void HeapSnapshot::Print(int max_depth) {
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001253 root()->Print("", "", max_depth, 0);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001254}
1255
1256
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001257template<typename T, class P>
1258static size_t GetMemoryUsedByList(const List<T, P>& list) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001259 return list.length() * sizeof(T) + sizeof(list);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001260}
1261
1262
1263size_t HeapSnapshot::RawSnapshotSize() const {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001264 STATIC_CHECK(SnapshotSizeConstants<kPointerSize>::kExpectedHeapSnapshotSize ==
1265 sizeof(HeapSnapshot)); // NOLINT
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001266 return
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001267 sizeof(*this) +
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001268 GetMemoryUsedByList(entries_) +
1269 GetMemoryUsedByList(edges_) +
1270 GetMemoryUsedByList(children_) +
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001271 GetMemoryUsedByList(sorted_entries_);
1272}
1273
1274
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001275// We split IDs on evens for embedder objects (see
1276// HeapObjectsMap::GenerateId) and odds for native objects.
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00001277const SnapshotObjectId HeapObjectsMap::kInternalRootObjectId = 1;
1278const SnapshotObjectId HeapObjectsMap::kGcRootsObjectId =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001279 HeapObjectsMap::kInternalRootObjectId + HeapObjectsMap::kObjectIdStep;
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00001280const SnapshotObjectId HeapObjectsMap::kGcRootsFirstSubrootId =
danno@chromium.orgfa458e42012-02-01 10:48:36 +00001281 HeapObjectsMap::kGcRootsObjectId + HeapObjectsMap::kObjectIdStep;
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00001282const SnapshotObjectId HeapObjectsMap::kFirstAvailableObjectId =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001283 HeapObjectsMap::kGcRootsFirstSubrootId +
1284 VisitorSynchronization::kNumberOfSyncTags * HeapObjectsMap::kObjectIdStep;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001285
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001286HeapObjectsMap::HeapObjectsMap()
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001287 : next_id_(kFirstAvailableObjectId),
1288 entries_map_(AddressesMatch) {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001289 // This dummy element solves a problem with entries_map_.
1290 // When we do lookup in HashMap we see no difference between two cases:
1291 // it has an entry with NULL as the value or it has created
1292 // a new entry on the fly with NULL as the default value.
1293 // With such dummy element we have a guaranty that all entries_map_ entries
1294 // will have the value field grater than 0.
1295 // This fact is using in MoveObject method.
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001296 entries_.Add(EntryInfo(0, NULL, 0));
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001297}
1298
1299
1300void HeapObjectsMap::SnapshotGenerationFinished() {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001301 RemoveDeadEntries();
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001302}
1303
1304
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001305void HeapObjectsMap::MoveObject(Address from, Address to) {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001306 ASSERT(to != NULL);
1307 ASSERT(from != NULL);
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001308 if (from == to) return;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001309 void* from_value = entries_map_.Remove(from, AddressHash(from));
1310 if (from_value == NULL) return;
1311 int from_entry_info_index =
1312 static_cast<int>(reinterpret_cast<intptr_t>(from_value));
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001313 entries_.at(from_entry_info_index).addr = to;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001314 HashMap::Entry* to_entry = entries_map_.Lookup(to, AddressHash(to), true);
1315 if (to_entry->value != NULL) {
1316 int to_entry_info_index =
1317 static_cast<int>(reinterpret_cast<intptr_t>(to_entry->value));
1318 // Without this operation we will have two EntryInfo's with the same
1319 // value in addr field. It is bad because later at RemoveDeadEntries
1320 // one of this entry will be removed with the corresponding entries_map_
1321 // entry.
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001322 entries_.at(to_entry_info_index).addr = NULL;
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001323 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001324 to_entry->value = reinterpret_cast<void*>(from_entry_info_index);
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001325}
1326
1327
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00001328SnapshotObjectId HeapObjectsMap::FindEntry(Address addr) {
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001329 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false);
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001330 if (entry == NULL) return 0;
1331 int entry_index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
1332 EntryInfo& entry_info = entries_.at(entry_index);
1333 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
1334 return entry_info.id;
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001335}
1336
1337
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001338SnapshotObjectId HeapObjectsMap::FindOrAddEntry(Address addr,
1339 unsigned int size) {
1340 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001341 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true);
1342 if (entry->value != NULL) {
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001343 int entry_index =
1344 static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001345 EntryInfo& entry_info = entries_.at(entry_index);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001346 entry_info.accessed = true;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001347 entry_info.size = size;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001348 return entry_info.id;
1349 }
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001350 entry->value = reinterpret_cast<void*>(entries_.length());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001351 SnapshotObjectId id = next_id_;
1352 next_id_ += kObjectIdStep;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001353 entries_.Add(EntryInfo(id, addr, size));
1354 ASSERT(static_cast<uint32_t>(entries_.length()) > entries_map_.occupancy());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001355 return id;
1356}
1357
1358
1359void HeapObjectsMap::StopHeapObjectsTracking() {
1360 time_intervals_.Clear();
1361}
1362
1363void HeapObjectsMap::UpdateHeapObjectsMap() {
1364 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask,
1365 "HeapSnapshotsCollection::UpdateHeapObjectsMap");
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001366 HeapIterator iterator;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001367 for (HeapObject* obj = iterator.next();
1368 obj != NULL;
1369 obj = iterator.next()) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001370 FindOrAddEntry(obj->address(), obj->Size());
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001371 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001372 RemoveDeadEntries();
1373}
1374
1375
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001376SnapshotObjectId HeapObjectsMap::PushHeapObjectsStats(OutputStream* stream) {
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001377 UpdateHeapObjectsMap();
1378 time_intervals_.Add(TimeInterval(next_id_));
1379 int prefered_chunk_size = stream->GetChunkSize();
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001380 List<v8::HeapStatsUpdate> stats_buffer;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001381 ASSERT(!entries_.is_empty());
1382 EntryInfo* entry_info = &entries_.first();
1383 EntryInfo* end_entry_info = &entries_.last() + 1;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001384 for (int time_interval_index = 0;
1385 time_interval_index < time_intervals_.length();
1386 ++time_interval_index) {
1387 TimeInterval& time_interval = time_intervals_[time_interval_index];
1388 SnapshotObjectId time_interval_id = time_interval.id;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001389 uint32_t entries_size = 0;
1390 EntryInfo* start_entry_info = entry_info;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001391 while (entry_info < end_entry_info && entry_info->id < time_interval_id) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001392 entries_size += entry_info->size;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001393 ++entry_info;
1394 }
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001395 uint32_t entries_count =
1396 static_cast<uint32_t>(entry_info - start_entry_info);
1397 if (time_interval.count != entries_count ||
1398 time_interval.size != entries_size) {
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001399 stats_buffer.Add(v8::HeapStatsUpdate(
1400 time_interval_index,
1401 time_interval.count = entries_count,
1402 time_interval.size = entries_size));
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001403 if (stats_buffer.length() >= prefered_chunk_size) {
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001404 OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001405 &stats_buffer.first(), stats_buffer.length());
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001406 if (result == OutputStream::kAbort) return last_assigned_id();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001407 stats_buffer.Clear();
1408 }
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001409 }
1410 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001411 ASSERT(entry_info == end_entry_info);
1412 if (!stats_buffer.is_empty()) {
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00001413 OutputStream::WriteResult result = stream->WriteHeapStatsChunk(
1414 &stats_buffer.first(), stats_buffer.length());
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001415 if (result == OutputStream::kAbort) return last_assigned_id();
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001416 }
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001417 stream->EndOfStream();
rossberg@chromium.org400388e2012-06-06 09:29:22 +00001418 return last_assigned_id();
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001419}
1420
1421
1422void HeapObjectsMap::RemoveDeadEntries() {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001423 ASSERT(entries_.length() > 0 &&
1424 entries_.at(0).id == 0 &&
1425 entries_.at(0).addr == NULL);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001426 int first_free_entry = 1;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001427 for (int i = 1; i < entries_.length(); ++i) {
1428 EntryInfo& entry_info = entries_.at(i);
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001429 if (entry_info.accessed) {
1430 if (first_free_entry != i) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001431 entries_.at(first_free_entry) = entry_info;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001432 }
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001433 entries_.at(first_free_entry).accessed = false;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001434 HashMap::Entry* entry = entries_map_.Lookup(
1435 entry_info.addr, AddressHash(entry_info.addr), false);
1436 ASSERT(entry);
1437 entry->value = reinterpret_cast<void*>(first_free_entry);
1438 ++first_free_entry;
1439 } else {
1440 if (entry_info.addr) {
1441 entries_map_.Remove(entry_info.addr, AddressHash(entry_info.addr));
1442 }
1443 }
1444 }
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001445 entries_.Rewind(first_free_entry);
1446 ASSERT(static_cast<uint32_t>(entries_.length()) - 1 ==
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00001447 entries_map_.occupancy());
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001448}
1449
1450
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00001451SnapshotObjectId HeapObjectsMap::GenerateId(v8::RetainedObjectInfo* info) {
1452 SnapshotObjectId id = static_cast<SnapshotObjectId>(info->GetHash());
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001453 const char* label = info->GetLabel();
rossberg@chromium.orgfab14982012-01-05 15:02:15 +00001454 id ^= HashSequentialString(label,
1455 static_cast<int>(strlen(label)),
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001456 HEAP->HashSeed());
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001457 intptr_t element_count = info->GetElementCount();
1458 if (element_count != -1)
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001459 id ^= ComputeIntegerHash(static_cast<uint32_t>(element_count),
1460 v8::internal::kZeroHashSeed);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001461 return id << 1;
1462}
1463
1464
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001465size_t HeapObjectsMap::GetUsedMemorySize() const {
1466 return
1467 sizeof(*this) +
1468 sizeof(HashMap::Entry) * entries_map_.capacity() +
1469 GetMemoryUsedByList(entries_) +
1470 GetMemoryUsedByList(time_intervals_);
1471}
1472
1473
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001474HeapSnapshotsCollection::HeapSnapshotsCollection()
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001475 : is_tracking_objects_(false),
1476 snapshots_uids_(HeapSnapshotsMatch),
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001477 token_enumerator_(new TokenEnumerator()) {
1478}
1479
1480
1481static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) {
1482 delete *snapshot_ptr;
1483}
1484
1485
1486HeapSnapshotsCollection::~HeapSnapshotsCollection() {
1487 delete token_enumerator_;
1488 snapshots_.Iterate(DeleteHeapSnapshot);
1489}
1490
1491
erik.corry@gmail.com145eff52010-08-23 11:36:18 +00001492HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(HeapSnapshot::Type type,
1493 const char* name,
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001494 unsigned uid) {
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001495 is_tracking_objects_ = true; // Start watching for heap objects moves.
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00001496 return new HeapSnapshot(this, type, name, uid);
1497}
1498
1499
1500void HeapSnapshotsCollection::SnapshotGenerationFinished(
1501 HeapSnapshot* snapshot) {
1502 ids_.SnapshotGenerationFinished();
1503 if (snapshot != NULL) {
1504 snapshots_.Add(snapshot);
1505 HashMap::Entry* entry =
1506 snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
1507 static_cast<uint32_t>(snapshot->uid()),
1508 true);
1509 ASSERT(entry->value == NULL);
1510 entry->value = snapshot;
1511 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001512}
1513
1514
1515HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) {
1516 HashMap::Entry* entry = snapshots_uids_.Lookup(reinterpret_cast<void*>(uid),
1517 static_cast<uint32_t>(uid),
1518 false);
1519 return entry != NULL ? reinterpret_cast<HeapSnapshot*>(entry->value) : NULL;
1520}
1521
1522
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001523void HeapSnapshotsCollection::RemoveSnapshot(HeapSnapshot* snapshot) {
1524 snapshots_.RemoveElement(snapshot);
1525 unsigned uid = snapshot->uid();
1526 snapshots_uids_.Remove(reinterpret_cast<void*>(uid),
1527 static_cast<uint32_t>(uid));
1528}
1529
1530
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00001531Handle<HeapObject> HeapSnapshotsCollection::FindHeapObjectById(
1532 SnapshotObjectId id) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001533 // First perform a full GC in order to avoid dead objects.
rossberg@chromium.org994edf62012-02-06 10:12:55 +00001534 HEAP->CollectAllGarbage(Heap::kMakeHeapIterableMask,
1535 "HeapSnapshotsCollection::FindHeapObjectById");
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001536 AssertNoAllocation no_allocation;
1537 HeapObject* object = NULL;
1538 HeapIterator iterator(HeapIterator::kFilterUnreachable);
1539 // Make sure that object with the given id is still reachable.
1540 for (HeapObject* obj = iterator.next();
1541 obj != NULL;
1542 obj = iterator.next()) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001543 if (ids_.FindEntry(obj->address()) == id) {
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001544 ASSERT(object == NULL);
1545 object = obj;
1546 // Can't break -- kFilterUnreachable requires full heap traversal.
1547 }
1548 }
1549 return object != NULL ? Handle<HeapObject>(object) : Handle<HeapObject>();
1550}
1551
1552
mmassi@chromium.org7028c052012-06-13 11:51:58 +00001553size_t HeapSnapshotsCollection::GetUsedMemorySize() const {
1554 STATIC_CHECK(SnapshotSizeConstants<kPointerSize>::
1555 kExpectedHeapSnapshotsCollectionSize ==
1556 sizeof(HeapSnapshotsCollection)); // NOLINT
1557 size_t size = sizeof(*this);
1558 size += names_.GetUsedMemorySize();
1559 size += ids_.GetUsedMemorySize();
1560 size += sizeof(HashMap::Entry) * snapshots_uids_.capacity();
1561 size += GetMemoryUsedByList(snapshots_);
1562 for (int i = 0; i < snapshots_.length(); ++i) {
1563 size += snapshots_[i]->RawSnapshotSize();
1564 }
1565 return size;
1566}
1567
1568
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001569HeapEntriesMap::HeapEntriesMap()
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001570 : entries_(HeapThingsMatch) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001571}
1572
1573
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001574int HeapEntriesMap::Map(HeapThing thing) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001575 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), false);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001576 if (cache_entry == NULL) return HeapEntry::kNoEntry;
1577 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001578}
1579
1580
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001581void HeapEntriesMap::Pair(HeapThing thing, int entry) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001582 HashMap::Entry* cache_entry = entries_.Lookup(thing, Hash(thing), true);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001583 ASSERT(cache_entry->value == NULL);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001584 cache_entry->value = reinterpret_cast<void*>(static_cast<intptr_t>(entry));
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001585}
1586
1587
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001588HeapObjectsSet::HeapObjectsSet()
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001589 : entries_(HeapEntriesMap::HeapThingsMatch) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001590}
1591
1592
1593void HeapObjectsSet::Clear() {
1594 entries_.Clear();
1595}
1596
1597
1598bool HeapObjectsSet::Contains(Object* obj) {
1599 if (!obj->IsHeapObject()) return false;
1600 HeapObject* object = HeapObject::cast(obj);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001601 return entries_.Lookup(object, HeapEntriesMap::Hash(object), false) != NULL;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001602}
1603
1604
1605void HeapObjectsSet::Insert(Object* obj) {
1606 if (!obj->IsHeapObject()) return;
1607 HeapObject* object = HeapObject::cast(obj);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001608 entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001609}
1610
1611
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001612const char* HeapObjectsSet::GetTag(Object* obj) {
1613 HeapObject* object = HeapObject::cast(obj);
1614 HashMap::Entry* cache_entry =
1615 entries_.Lookup(object, HeapEntriesMap::Hash(object), false);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001616 return cache_entry != NULL
1617 ? reinterpret_cast<const char*>(cache_entry->value)
1618 : NULL;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001619}
1620
1621
1622void HeapObjectsSet::SetTag(Object* obj, const char* tag) {
1623 if (!obj->IsHeapObject()) return;
1624 HeapObject* object = HeapObject::cast(obj);
1625 HashMap::Entry* cache_entry =
1626 entries_.Lookup(object, HeapEntriesMap::Hash(object), true);
1627 cache_entry->value = const_cast<char*>(tag);
1628}
1629
1630
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001631HeapObject* const V8HeapExplorer::kInternalRootObject =
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001632 reinterpret_cast<HeapObject*>(
1633 static_cast<intptr_t>(HeapObjectsMap::kInternalRootObjectId));
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001634HeapObject* const V8HeapExplorer::kGcRootsObject =
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001635 reinterpret_cast<HeapObject*>(
1636 static_cast<intptr_t>(HeapObjectsMap::kGcRootsObjectId));
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001637HeapObject* const V8HeapExplorer::kFirstGcSubrootObject =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001638 reinterpret_cast<HeapObject*>(
1639 static_cast<intptr_t>(HeapObjectsMap::kGcRootsFirstSubrootId));
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00001640HeapObject* const V8HeapExplorer::kLastGcSubrootObject =
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001641 reinterpret_cast<HeapObject*>(
1642 static_cast<intptr_t>(HeapObjectsMap::kFirstAvailableObjectId));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001643
1644
1645V8HeapExplorer::V8HeapExplorer(
1646 HeapSnapshot* snapshot,
1647 SnapshottingProgressReportingInterface* progress)
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001648 : heap_(Isolate::Current()->heap()),
1649 snapshot_(snapshot),
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001650 collection_(snapshot_->collection()),
1651 progress_(progress),
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001652 filler_(NULL) {
1653}
1654
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001655
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001656V8HeapExplorer::~V8HeapExplorer() {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001657}
1658
1659
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001660HeapEntry* V8HeapExplorer::AllocateEntry(HeapThing ptr) {
1661 return AddEntry(reinterpret_cast<HeapObject*>(ptr));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001662}
1663
1664
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001665HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001666 if (object == kInternalRootObject) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001667 snapshot_->AddRootEntry();
1668 return snapshot_->root();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001669 } else if (object == kGcRootsObject) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001670 HeapEntry* entry = snapshot_->AddGcRootsEntry();
1671 return entry;
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001672 } else if (object >= kFirstGcSubrootObject && object < kLastGcSubrootObject) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001673 HeapEntry* entry = snapshot_->AddGcSubrootEntry(GetGcSubrootOrder(object));
1674 return entry;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001675 } else if (object->IsJSFunction()) {
1676 JSFunction* func = JSFunction::cast(object);
1677 SharedFunctionInfo* shared = func->shared();
ulan@chromium.org65a89c22012-02-14 11:46:07 +00001678 const char* name = shared->bound() ? "native_bind" :
1679 collection_->names()->GetName(String::cast(shared->name()));
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001680 return AddEntry(object, HeapEntry::kClosure, name);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001681 } else if (object->IsJSRegExp()) {
1682 JSRegExp* re = JSRegExp::cast(object);
1683 return AddEntry(object,
1684 HeapEntry::kRegExp,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001685 collection_->names()->GetName(re->Pattern()));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001686 } else if (object->IsJSObject()) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001687 const char* name = collection_->names()->GetName(
1688 GetConstructorName(JSObject::cast(object)));
1689 if (object->IsJSGlobalObject()) {
1690 const char* tag = objects_tags_.GetTag(object);
1691 if (tag != NULL) {
1692 name = collection_->names()->GetFormatted("%s / %s", name, tag);
1693 }
1694 }
1695 return AddEntry(object, HeapEntry::kObject, name);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001696 } else if (object->IsString()) {
1697 return AddEntry(object,
1698 HeapEntry::kString,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001699 collection_->names()->GetName(String::cast(object)));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001700 } else if (object->IsCode()) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001701 return AddEntry(object, HeapEntry::kCode, "");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001702 } else if (object->IsSharedFunctionInfo()) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001703 String* name = String::cast(SharedFunctionInfo::cast(object)->name());
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001704 return AddEntry(object,
1705 HeapEntry::kCode,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001706 collection_->names()->GetName(name));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001707 } else if (object->IsScript()) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001708 Object* name = Script::cast(object)->name();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001709 return AddEntry(object,
1710 HeapEntry::kCode,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001711 name->IsString()
1712 ? collection_->names()->GetName(String::cast(name))
1713 : "");
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001714 } else if (object->IsGlobalContext()) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001715 return AddEntry(object, HeapEntry::kHidden, "system / GlobalContext");
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001716 } else if (object->IsContext()) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001717 return AddEntry(object, HeapEntry::kHidden, "system / Context");
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001718 } else if (object->IsFixedArray() ||
1719 object->IsFixedDoubleArray() ||
1720 object->IsByteArray() ||
1721 object->IsExternalArray()) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001722 return AddEntry(object, HeapEntry::kArray, "");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001723 } else if (object->IsHeapNumber()) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001724 return AddEntry(object, HeapEntry::kHeapNumber, "number");
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001725 }
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001726 return AddEntry(object, HeapEntry::kHidden, GetSystemEntryName(object));
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001727}
1728
1729
1730HeapEntry* V8HeapExplorer::AddEntry(HeapObject* object,
1731 HeapEntry::Type type,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001732 const char* name) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00001733 int object_size = object->Size();
1734 SnapshotObjectId object_id =
1735 collection_->GetObjectId(object->address(), object_size);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001736 return snapshot_->AddEntry(type, name, object_id, object_size);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001737}
1738
1739
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001740class GcSubrootsEnumerator : public ObjectVisitor {
1741 public:
1742 GcSubrootsEnumerator(
1743 SnapshotFillerInterface* filler, V8HeapExplorer* explorer)
1744 : filler_(filler),
1745 explorer_(explorer),
1746 previous_object_count_(0),
1747 object_count_(0) {
1748 }
1749 void VisitPointers(Object** start, Object** end) {
1750 object_count_ += end - start;
1751 }
1752 void Synchronize(VisitorSynchronization::SyncTag tag) {
1753 // Skip empty subroots.
1754 if (previous_object_count_ != object_count_) {
1755 previous_object_count_ = object_count_;
1756 filler_->AddEntry(V8HeapExplorer::GetNthGcSubrootObject(tag), explorer_);
1757 }
1758 }
1759 private:
1760 SnapshotFillerInterface* filler_;
1761 V8HeapExplorer* explorer_;
1762 intptr_t previous_object_count_;
1763 intptr_t object_count_;
1764};
1765
1766
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001767void V8HeapExplorer::AddRootEntries(SnapshotFillerInterface* filler) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00001768 filler->AddEntry(kInternalRootObject, this);
1769 filler->AddEntry(kGcRootsObject, this);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00001770 GcSubrootsEnumerator enumerator(filler, this);
1771 heap_->IterateRoots(&enumerator, VISIT_ALL);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001772}
1773
1774
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001775const char* V8HeapExplorer::GetSystemEntryName(HeapObject* object) {
1776 switch (object->map()->instance_type()) {
1777 case MAP_TYPE: return "system / Map";
1778 case JS_GLOBAL_PROPERTY_CELL_TYPE: return "system / JSGlobalPropertyCell";
ager@chromium.orgea91cc52011-05-23 06:06:11 +00001779 case FOREIGN_TYPE: return "system / Foreign";
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001780 case ODDBALL_TYPE: return "system / Oddball";
1781#define MAKE_STRUCT_CASE(NAME, Name, name) \
1782 case NAME##_TYPE: return "system / "#Name;
1783 STRUCT_LIST(MAKE_STRUCT_CASE)
1784#undef MAKE_STRUCT_CASE
1785 default: return "system";
1786 }
1787}
1788
1789
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001790int V8HeapExplorer::EstimateObjectsCount(HeapIterator* iterator) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001791 int objects_count = 0;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001792 for (HeapObject* obj = iterator->next();
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001793 obj != NULL;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00001794 obj = iterator->next()) {
1795 objects_count++;
1796 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001797 return objects_count;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001798}
1799
1800
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001801class IndexedReferencesExtractor : public ObjectVisitor {
1802 public:
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001803 IndexedReferencesExtractor(V8HeapExplorer* generator,
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001804 HeapObject* parent_obj,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001805 int parent)
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001806 : generator_(generator),
1807 parent_obj_(parent_obj),
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001808 parent_(parent),
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001809 next_index_(1) {
1810 }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001811 void VisitPointers(Object** start, Object** end) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001812 for (Object** p = start; p < end; p++) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001813 if (CheckVisitedAndUnmark(p)) continue;
1814 generator_->SetHiddenReference(parent_obj_, parent_, next_index_++, *p);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001815 }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001816 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001817 static void MarkVisitedField(HeapObject* obj, int offset) {
1818 if (offset < 0) return;
1819 Address field = obj->address() + offset;
1820 ASSERT(!Memory::Object_at(field)->IsFailure());
1821 ASSERT(Memory::Object_at(field)->IsHeapObject());
1822 *field |= kFailureTag;
1823 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00001824
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001825 private:
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001826 bool CheckVisitedAndUnmark(Object** field) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001827 if ((*field)->IsFailure()) {
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00001828 intptr_t untagged = reinterpret_cast<intptr_t>(*field) & ~kFailureTagMask;
1829 *field = reinterpret_cast<Object*>(untagged | kHeapObjectTag);
1830 ASSERT((*field)->IsHeapObject());
1831 return true;
1832 }
1833 return false;
1834 }
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001835 V8HeapExplorer* generator_;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001836 HeapObject* parent_obj_;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001837 int parent_;
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001838 int next_index_;
1839};
1840
1841
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00001842void V8HeapExplorer::ExtractReferences(HeapObject* obj) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001843 HeapEntry* heap_entry = GetEntry(obj);
1844 if (heap_entry == NULL) return; // No interest in this object.
1845 int entry = heap_entry->index();
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001846
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001847 bool extract_indexed_refs = true;
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001848 if (obj->IsJSGlobalProxy()) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001849 ExtractJSGlobalProxyReferences(JSGlobalProxy::cast(obj));
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00001850 } else if (obj->IsJSObject()) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001851 ExtractJSObjectReferences(entry, JSObject::cast(obj));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001852 } else if (obj->IsString()) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001853 ExtractStringReferences(entry, String::cast(obj));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001854 extract_indexed_refs = false;
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001855 } else if (obj->IsContext()) {
1856 ExtractContextReferences(entry, Context::cast(obj));
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001857 } else if (obj->IsMap()) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001858 ExtractMapReferences(entry, Map::cast(obj));
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001859 } else if (obj->IsSharedFunctionInfo()) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001860 ExtractSharedFunctionInfoReferences(entry, SharedFunctionInfo::cast(obj));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001861 } else if (obj->IsScript()) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001862 ExtractScriptReferences(entry, Script::cast(obj));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001863 } else if (obj->IsCodeCache()) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001864 ExtractCodeCacheReferences(entry, CodeCache::cast(obj));
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001865 } else if (obj->IsCode()) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001866 ExtractCodeReferences(entry, Code::cast(obj));
1867 } else if (obj->IsJSGlobalPropertyCell()) {
1868 ExtractJSGlobalPropertyCellReferences(
1869 entry, JSGlobalPropertyCell::cast(obj));
1870 extract_indexed_refs = false;
ricow@chromium.org4f693d62011-07-04 14:01:31 +00001871 }
1872 if (extract_indexed_refs) {
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00001873 SetInternalReference(obj, entry, "map", obj->map(), HeapObject::kMapOffset);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00001874 IndexedReferencesExtractor refs_extractor(this, obj, entry);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001875 obj->Iterate(&refs_extractor);
1876 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001877}
1878
1879
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001880void V8HeapExplorer::ExtractJSGlobalProxyReferences(JSGlobalProxy* proxy) {
1881 // We need to reference JS global objects from snapshot's root.
1882 // We use JSGlobalProxy because this is what embedder (e.g. browser)
1883 // uses for the global object.
1884 Object* object = proxy->map()->prototype();
1885 bool is_debug_object = false;
1886#ifdef ENABLE_DEBUGGER_SUPPORT
1887 is_debug_object = object->IsGlobalObject() &&
1888 Isolate::Current()->debug()->IsDebugGlobal(GlobalObject::cast(object));
1889#endif
1890 if (!is_debug_object) {
1891 SetUserGlobalReference(object);
1892 }
1893}
1894
1895
1896void V8HeapExplorer::ExtractJSObjectReferences(
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001897 int entry, JSObject* js_obj) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001898 HeapObject* obj = js_obj;
1899 ExtractClosureReferences(js_obj, entry);
1900 ExtractPropertyReferences(js_obj, entry);
1901 ExtractElementReferences(js_obj, entry);
1902 ExtractInternalReferences(js_obj, entry);
1903 SetPropertyReference(
1904 obj, entry, heap_->Proto_symbol(), js_obj->GetPrototype());
1905 if (obj->IsJSFunction()) {
1906 JSFunction* js_fun = JSFunction::cast(js_obj);
1907 Object* proto_or_map = js_fun->prototype_or_initial_map();
1908 if (!proto_or_map->IsTheHole()) {
1909 if (!proto_or_map->IsMap()) {
1910 SetPropertyReference(
1911 obj, entry,
1912 heap_->prototype_symbol(), proto_or_map,
1913 NULL,
1914 JSFunction::kPrototypeOrInitialMapOffset);
1915 } else {
1916 SetPropertyReference(
1917 obj, entry,
1918 heap_->prototype_symbol(), js_fun->prototype());
1919 }
1920 }
1921 SharedFunctionInfo* shared_info = js_fun->shared();
1922 // JSFunction has either bindings or literals and never both.
1923 bool bound = shared_info->bound();
1924 TagObject(js_fun->literals_or_bindings(),
1925 bound ? "(function bindings)" : "(function literals)");
1926 SetInternalReference(js_fun, entry,
1927 bound ? "bindings" : "literals",
1928 js_fun->literals_or_bindings(),
1929 JSFunction::kLiteralsOffset);
1930 TagObject(shared_info, "(shared function info)");
1931 SetInternalReference(js_fun, entry,
1932 "shared", shared_info,
1933 JSFunction::kSharedFunctionInfoOffset);
1934 TagObject(js_fun->unchecked_context(), "(context)");
1935 SetInternalReference(js_fun, entry,
1936 "context", js_fun->unchecked_context(),
1937 JSFunction::kContextOffset);
1938 for (int i = JSFunction::kNonWeakFieldsEndOffset;
1939 i < JSFunction::kSize;
1940 i += kPointerSize) {
1941 SetWeakReference(js_fun, entry, i, *HeapObject::RawField(js_fun, i), i);
1942 }
1943 } else if (obj->IsGlobalObject()) {
1944 GlobalObject* global_obj = GlobalObject::cast(obj);
1945 SetInternalReference(global_obj, entry,
1946 "builtins", global_obj->builtins(),
1947 GlobalObject::kBuiltinsOffset);
1948 SetInternalReference(global_obj, entry,
1949 "global_context", global_obj->global_context(),
1950 GlobalObject::kGlobalContextOffset);
1951 SetInternalReference(global_obj, entry,
1952 "global_receiver", global_obj->global_receiver(),
1953 GlobalObject::kGlobalReceiverOffset);
1954 }
1955 TagObject(js_obj->properties(), "(object properties)");
1956 SetInternalReference(obj, entry,
1957 "properties", js_obj->properties(),
1958 JSObject::kPropertiesOffset);
1959 TagObject(js_obj->elements(), "(object elements)");
1960 SetInternalReference(obj, entry,
1961 "elements", js_obj->elements(),
1962 JSObject::kElementsOffset);
1963}
1964
1965
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001966void V8HeapExplorer::ExtractStringReferences(int entry, String* string) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001967 if (string->IsConsString()) {
1968 ConsString* cs = ConsString::cast(string);
1969 SetInternalReference(cs, entry, "first", cs->first());
1970 SetInternalReference(cs, entry, "second", cs->second());
1971 } else if (string->IsSlicedString()) {
1972 SlicedString* ss = SlicedString::cast(string);
1973 SetInternalReference(ss, entry, "parent", ss->parent());
1974 }
1975}
1976
1977
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00001978void V8HeapExplorer::ExtractContextReferences(int entry, Context* context) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00001979#define EXTRACT_CONTEXT_FIELD(index, type, name) \
1980 SetInternalReference(context, entry, #name, context->get(Context::index), \
1981 FixedArray::OffsetOfElementAt(Context::index));
1982 EXTRACT_CONTEXT_FIELD(CLOSURE_INDEX, JSFunction, closure);
1983 EXTRACT_CONTEXT_FIELD(PREVIOUS_INDEX, Context, previous);
1984 EXTRACT_CONTEXT_FIELD(EXTENSION_INDEX, Object, extension);
1985 EXTRACT_CONTEXT_FIELD(GLOBAL_INDEX, GlobalObject, global);
1986 if (context->IsGlobalContext()) {
1987 TagObject(context->jsfunction_result_caches(),
1988 "(context func. result caches)");
1989 TagObject(context->normalized_map_cache(), "(context norm. map cache)");
1990 TagObject(context->runtime_context(), "(runtime context)");
1991 TagObject(context->data(), "(context data)");
1992 GLOBAL_CONTEXT_FIELDS(EXTRACT_CONTEXT_FIELD);
1993#undef EXTRACT_CONTEXT_FIELD
1994 for (int i = Context::FIRST_WEAK_SLOT;
1995 i < Context::GLOBAL_CONTEXT_SLOTS;
1996 ++i) {
1997 SetWeakReference(context, entry, i, context->get(i),
1998 FixedArray::OffsetOfElementAt(i));
1999 }
2000 }
2001}
2002
2003
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002004void V8HeapExplorer::ExtractMapReferences(int entry, Map* map) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002005 SetInternalReference(map, entry,
2006 "prototype", map->prototype(), Map::kPrototypeOffset);
2007 SetInternalReference(map, entry,
2008 "constructor", map->constructor(),
2009 Map::kConstructorOffset);
2010 if (!map->instance_descriptors()->IsEmpty()) {
2011 TagObject(map->instance_descriptors(), "(map descriptors)");
2012 SetInternalReference(map, entry,
2013 "descriptors", map->instance_descriptors(),
rossberg@chromium.org657d53b2012-07-12 11:06:03 +00002014 Map::kInstanceDescriptorsOrBackPointerOffset);
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002015 }
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002016 SetInternalReference(map, entry,
2017 "code_cache", map->code_cache(),
2018 Map::kCodeCacheOffset);
2019}
2020
2021
2022void V8HeapExplorer::ExtractSharedFunctionInfoReferences(
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002023 int entry, SharedFunctionInfo* shared) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002024 HeapObject* obj = shared;
2025 SetInternalReference(obj, entry,
2026 "name", shared->name(),
2027 SharedFunctionInfo::kNameOffset);
2028 TagObject(shared->code(), "(code)");
2029 SetInternalReference(obj, entry,
2030 "code", shared->code(),
2031 SharedFunctionInfo::kCodeOffset);
2032 TagObject(shared->scope_info(), "(function scope info)");
2033 SetInternalReference(obj, entry,
2034 "scope_info", shared->scope_info(),
2035 SharedFunctionInfo::kScopeInfoOffset);
2036 SetInternalReference(obj, entry,
2037 "instance_class_name", shared->instance_class_name(),
2038 SharedFunctionInfo::kInstanceClassNameOffset);
2039 SetInternalReference(obj, entry,
2040 "script", shared->script(),
2041 SharedFunctionInfo::kScriptOffset);
2042 TagObject(shared->construct_stub(), "(code)");
2043 SetInternalReference(obj, entry,
2044 "construct_stub", shared->construct_stub(),
2045 SharedFunctionInfo::kConstructStubOffset);
2046 SetInternalReference(obj, entry,
2047 "function_data", shared->function_data(),
2048 SharedFunctionInfo::kFunctionDataOffset);
2049 SetInternalReference(obj, entry,
2050 "debug_info", shared->debug_info(),
2051 SharedFunctionInfo::kDebugInfoOffset);
2052 SetInternalReference(obj, entry,
2053 "inferred_name", shared->inferred_name(),
2054 SharedFunctionInfo::kInferredNameOffset);
2055 SetInternalReference(obj, entry,
2056 "this_property_assignments",
2057 shared->this_property_assignments(),
2058 SharedFunctionInfo::kThisPropertyAssignmentsOffset);
2059 SetWeakReference(obj, entry,
2060 1, shared->initial_map(),
2061 SharedFunctionInfo::kInitialMapOffset);
2062}
2063
2064
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002065void V8HeapExplorer::ExtractScriptReferences(int entry, Script* script) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002066 HeapObject* obj = script;
2067 SetInternalReference(obj, entry,
2068 "source", script->source(),
2069 Script::kSourceOffset);
2070 SetInternalReference(obj, entry,
2071 "name", script->name(),
2072 Script::kNameOffset);
2073 SetInternalReference(obj, entry,
2074 "data", script->data(),
2075 Script::kDataOffset);
2076 SetInternalReference(obj, entry,
2077 "context_data", script->context_data(),
2078 Script::kContextOffset);
2079 TagObject(script->line_ends(), "(script line ends)");
2080 SetInternalReference(obj, entry,
2081 "line_ends", script->line_ends(),
2082 Script::kLineEndsOffset);
2083}
2084
2085
2086void V8HeapExplorer::ExtractCodeCacheReferences(
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002087 int entry, CodeCache* code_cache) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002088 TagObject(code_cache->default_cache(), "(default code cache)");
2089 SetInternalReference(code_cache, entry,
2090 "default_cache", code_cache->default_cache(),
2091 CodeCache::kDefaultCacheOffset);
2092 TagObject(code_cache->normal_type_cache(), "(code type cache)");
2093 SetInternalReference(code_cache, entry,
2094 "type_cache", code_cache->normal_type_cache(),
2095 CodeCache::kNormalTypeCacheOffset);
2096}
2097
2098
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002099void V8HeapExplorer::ExtractCodeReferences(int entry, Code* code) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002100 TagObject(code->relocation_info(), "(code relocation info)");
2101 SetInternalReference(code, entry,
2102 "relocation_info", code->relocation_info(),
2103 Code::kRelocationInfoOffset);
2104 SetInternalReference(code, entry,
2105 "handler_table", code->handler_table(),
2106 Code::kHandlerTableOffset);
2107 TagObject(code->deoptimization_data(), "(code deopt data)");
2108 SetInternalReference(code, entry,
2109 "deoptimization_data", code->deoptimization_data(),
2110 Code::kDeoptimizationDataOffset);
2111 SetInternalReference(code, entry,
2112 "type_feedback_info", code->type_feedback_info(),
2113 Code::kTypeFeedbackInfoOffset);
2114 SetInternalReference(code, entry,
2115 "gc_metadata", code->gc_metadata(),
2116 Code::kGCMetadataOffset);
2117}
2118
2119
2120void V8HeapExplorer::ExtractJSGlobalPropertyCellReferences(
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002121 int entry, JSGlobalPropertyCell* cell) {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002122 SetInternalReference(cell, entry, "value", cell->value());
2123}
2124
2125
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002126void V8HeapExplorer::ExtractClosureReferences(JSObject* js_obj, int entry) {
ulan@chromium.org65a89c22012-02-14 11:46:07 +00002127 if (!js_obj->IsJSFunction()) return;
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002128
ulan@chromium.org65a89c22012-02-14 11:46:07 +00002129 JSFunction* func = JSFunction::cast(js_obj);
ulan@chromium.org65a89c22012-02-14 11:46:07 +00002130 if (func->shared()->bound()) {
2131 FixedArray* bindings = func->function_bindings();
2132 SetNativeBindReference(js_obj, entry, "bound_this",
2133 bindings->get(JSFunction::kBoundThisIndex));
2134 SetNativeBindReference(js_obj, entry, "bound_function",
2135 bindings->get(JSFunction::kBoundFunctionIndex));
2136 for (int i = JSFunction::kBoundArgumentsStartIndex;
2137 i < bindings->length(); i++) {
2138 const char* reference_name = collection_->names()->GetFormatted(
2139 "bound_argument_%d",
2140 i - JSFunction::kBoundArgumentsStartIndex);
2141 SetNativeBindReference(js_obj, entry, reference_name,
2142 bindings->get(i));
2143 }
2144 } else {
yangguo@chromium.orgefdb9d72012-04-26 08:21:05 +00002145 Context* context = func->context()->declaration_context();
2146 ScopeInfo* scope_info = context->closure()->shared()->scope_info();
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002147 // Add context allocated locals.
2148 int context_locals = scope_info->ContextLocalCount();
2149 for (int i = 0; i < context_locals; ++i) {
2150 String* local_name = scope_info->ContextLocalName(i);
2151 int idx = Context::MIN_CONTEXT_SLOTS + i;
2152 SetClosureReference(js_obj, entry, local_name, context->get(idx));
2153 }
2154
2155 // Add function variable.
2156 if (scope_info->HasFunctionName()) {
2157 String* name = scope_info->FunctionName();
jkummerow@chromium.orgc3b37122011-11-07 10:14:12 +00002158 VariableMode mode;
jkummerow@chromium.org28faa982012-04-13 09:58:30 +00002159 int idx = scope_info->FunctionContextSlotIndex(name, &mode);
2160 if (idx >= 0) {
2161 SetClosureReference(js_obj, entry, name, context->get(idx));
2162 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002163 }
2164 }
2165}
2166
2167
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002168void V8HeapExplorer::ExtractPropertyReferences(JSObject* js_obj, int entry) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002169 if (js_obj->HasFastProperties()) {
2170 DescriptorArray* descs = js_obj->map()->instance_descriptors();
2171 for (int i = 0; i < descs->number_of_descriptors(); i++) {
2172 switch (descs->GetType(i)) {
2173 case FIELD: {
2174 int index = descs->GetFieldIndex(i);
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00002175
2176 String* k = descs->GetKey(i);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002177 if (index < js_obj->map()->inobject_properties()) {
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00002178 Object* value = js_obj->InObjectPropertyAt(index);
2179 if (k != heap_->hidden_symbol()) {
2180 SetPropertyReference(
2181 js_obj, entry,
2182 k, value,
2183 NULL,
2184 js_obj->GetInObjectPropertyOffset(index));
2185 } else {
2186 TagObject(value, "(hidden properties)");
2187 SetInternalReference(
2188 js_obj, entry,
2189 "hidden_properties", value,
2190 js_obj->GetInObjectPropertyOffset(index));
2191 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002192 } else {
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00002193 Object* value = js_obj->FastPropertyAt(index);
2194 if (k != heap_->hidden_symbol()) {
2195 SetPropertyReference(js_obj, entry, k, value);
2196 } else {
2197 TagObject(value, "(hidden properties)");
2198 SetInternalReference(js_obj, entry, "hidden_properties", value);
2199 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002200 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002201 break;
2202 }
2203 case CONSTANT_FUNCTION:
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002204 SetPropertyReference(
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002205 js_obj, entry,
2206 descs->GetKey(i), descs->GetConstantFunction(i));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002207 break;
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00002208 case CALLBACKS: {
2209 Object* callback_obj = descs->GetValue(i);
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00002210 if (callback_obj->IsAccessorPair()) {
2211 AccessorPair* accessors = AccessorPair::cast(callback_obj);
2212 if (Object* getter = accessors->getter()) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00002213 SetPropertyReference(js_obj, entry, descs->GetKey(i),
2214 getter, "get-%s");
2215 }
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00002216 if (Object* setter = accessors->setter()) {
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00002217 SetPropertyReference(js_obj, entry, descs->GetKey(i),
2218 setter, "set-%s");
2219 }
2220 }
2221 break;
2222 }
danno@chromium.orgc612e022011-11-10 11:38:15 +00002223 case NORMAL: // only in slow mode
2224 case HANDLER: // only in lookup results, not in descriptors
2225 case INTERCEPTOR: // only in lookup results, not in descriptors
jkummerow@chromium.org7a6fc812012-06-27 11:12:38 +00002226 break;
yangguo@chromium.org99aa4902012-07-06 16:21:55 +00002227 case TRANSITION:
jkummerow@chromium.org7a6fc812012-06-27 11:12:38 +00002228 case NONEXISTENT:
2229 UNREACHABLE();
danno@chromium.orgc612e022011-11-10 11:38:15 +00002230 break;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002231 }
2232 }
2233 } else {
2234 StringDictionary* dictionary = js_obj->property_dictionary();
2235 int length = dictionary->Capacity();
2236 for (int i = 0; i < length; ++i) {
2237 Object* k = dictionary->KeyAt(i);
2238 if (dictionary->IsKey(k)) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002239 Object* target = dictionary->ValueAt(i);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002240 // We assume that global objects can only have slow properties.
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00002241 Object* value = target->IsJSGlobalPropertyCell()
2242 ? JSGlobalPropertyCell::cast(target)->value()
2243 : target;
mstarzinger@chromium.orgc6d9cee2012-07-03 10:03:19 +00002244 if (k != heap_->hidden_symbol()) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00002245 SetPropertyReference(js_obj, entry, String::cast(k), value);
2246 } else {
2247 TagObject(value, "(hidden properties)");
2248 SetInternalReference(js_obj, entry, "hidden_properties", value);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002249 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002250 }
2251 }
2252 }
2253}
2254
2255
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002256void V8HeapExplorer::ExtractElementReferences(JSObject* js_obj, int entry) {
svenpanne@chromium.org830d30c2012-05-29 13:20:14 +00002257 if (js_obj->HasFastObjectElements()) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002258 FixedArray* elements = FixedArray::cast(js_obj->elements());
2259 int length = js_obj->IsJSArray() ?
2260 Smi::cast(JSArray::cast(js_obj)->length())->value() :
2261 elements->length();
2262 for (int i = 0; i < length; ++i) {
2263 if (!elements->get(i)->IsTheHole()) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002264 SetElementReference(js_obj, entry, i, elements->get(i));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002265 }
2266 }
2267 } else if (js_obj->HasDictionaryElements()) {
erik.corry@gmail.comf2038fb2012-01-16 11:42:08 +00002268 SeededNumberDictionary* dictionary = js_obj->element_dictionary();
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002269 int length = dictionary->Capacity();
2270 for (int i = 0; i < length; ++i) {
2271 Object* k = dictionary->KeyAt(i);
2272 if (dictionary->IsKey(k)) {
2273 ASSERT(k->IsNumber());
2274 uint32_t index = static_cast<uint32_t>(k->Number());
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002275 SetElementReference(js_obj, entry, index, dictionary->ValueAt(i));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00002276 }
2277 }
2278 }
2279}
2280
ricow@chromium.org4980dff2010-07-19 08:33:45 +00002281
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002282void V8HeapExplorer::ExtractInternalReferences(JSObject* js_obj, int entry) {
vegorov@chromium.org42841962010-10-18 11:18:59 +00002283 int length = js_obj->GetInternalFieldCount();
2284 for (int i = 0; i < length; ++i) {
2285 Object* o = js_obj->GetInternalField(i);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002286 SetInternalReference(
2287 js_obj, entry, i, o, js_obj->GetInternalFieldOffset(i));
vegorov@chromium.org42841962010-10-18 11:18:59 +00002288 }
2289}
2290
2291
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00002292String* V8HeapExplorer::GetConstructorName(JSObject* object) {
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002293 Heap* heap = object->GetHeap();
2294 if (object->IsJSFunction()) return heap->closure_symbol();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00002295 String* constructor_name = object->constructor_name();
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002296 if (constructor_name == heap->Object_symbol()) {
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00002297 // Look up an immediate "constructor" property, if it is a function,
2298 // return its name. This is for instances of binding objects, which
2299 // have prototype constructor type "Object".
2300 Object* constructor_prop = NULL;
erik.corry@gmail.com394dbcf2011-10-27 07:38:48 +00002301 LookupResult result(heap->isolate());
2302 object->LocalLookupRealNamedProperty(heap->constructor_symbol(), &result);
verwaest@chromium.org753aee42012-07-17 16:15:42 +00002303 if (!result.IsFound()) return object->constructor_name();
2304
2305 constructor_prop = result.GetLazyValue();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00002306 if (constructor_prop->IsJSFunction()) {
verwaest@chromium.org753aee42012-07-17 16:15:42 +00002307 Object* maybe_name =
2308 JSFunction::cast(constructor_prop)->shared()->name();
ricow@chromium.orgddd545c2011-08-24 12:02:41 +00002309 if (maybe_name->IsString()) {
2310 String* name = String::cast(maybe_name);
2311 if (name->length() > 0) return name;
2312 }
2313 }
2314 }
2315 return object->constructor_name();
2316}
2317
2318
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002319HeapEntry* V8HeapExplorer::GetEntry(Object* obj) {
2320 if (!obj->IsHeapObject()) return NULL;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002321 return filler_->FindOrAddEntry(obj, this);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002322}
2323
2324
2325class RootsReferencesExtractor : public ObjectVisitor {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002326 private:
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002327 struct IndexTag {
2328 IndexTag(int index, VisitorSynchronization::SyncTag tag)
2329 : index(index), tag(tag) { }
2330 int index;
2331 VisitorSynchronization::SyncTag tag;
2332 };
2333
2334 public:
2335 RootsReferencesExtractor()
2336 : collecting_all_references_(false),
2337 previous_reference_count_(0) {
2338 }
2339
2340 void VisitPointers(Object** start, Object** end) {
2341 if (collecting_all_references_) {
2342 for (Object** p = start; p < end; p++) all_references_.Add(*p);
2343 } else {
2344 for (Object** p = start; p < end; p++) strong_references_.Add(*p);
2345 }
2346 }
2347
2348 void SetCollectingAllReferences() { collecting_all_references_ = true; }
2349
2350 void FillReferences(V8HeapExplorer* explorer) {
2351 ASSERT(strong_references_.length() <= all_references_.length());
2352 for (int i = 0; i < reference_tags_.length(); ++i) {
2353 explorer->SetGcRootsReference(reference_tags_[i].tag);
2354 }
2355 int strong_index = 0, all_index = 0, tags_index = 0;
2356 while (all_index < all_references_.length()) {
2357 if (strong_index < strong_references_.length() &&
2358 strong_references_[strong_index] == all_references_[all_index]) {
2359 explorer->SetGcSubrootReference(reference_tags_[tags_index].tag,
2360 false,
2361 all_references_[all_index++]);
2362 ++strong_index;
2363 } else {
2364 explorer->SetGcSubrootReference(reference_tags_[tags_index].tag,
2365 true,
2366 all_references_[all_index++]);
2367 }
2368 if (reference_tags_[tags_index].index == all_index) ++tags_index;
2369 }
2370 }
2371
2372 void Synchronize(VisitorSynchronization::SyncTag tag) {
2373 if (collecting_all_references_ &&
2374 previous_reference_count_ != all_references_.length()) {
2375 previous_reference_count_ = all_references_.length();
2376 reference_tags_.Add(IndexTag(previous_reference_count_, tag));
2377 }
2378 }
2379
2380 private:
2381 bool collecting_all_references_;
2382 List<Object*> strong_references_;
2383 List<Object*> all_references_;
2384 int previous_reference_count_;
2385 List<IndexTag> reference_tags_;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002386};
2387
2388
2389bool V8HeapExplorer::IterateAndExtractReferences(
2390 SnapshotFillerInterface* filler) {
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002391 HeapIterator iterator(HeapIterator::kFilterUnreachable);
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002392
2393 filler_ = filler;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002394 bool interrupted = false;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00002395
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002396 // Heap iteration with filtering must be finished in any case.
2397 for (HeapObject* obj = iterator.next();
2398 obj != NULL;
2399 obj = iterator.next(), progress_->ProgressStep()) {
2400 if (!interrupted) {
2401 ExtractReferences(obj);
2402 if (!progress_->ProgressReport(false)) interrupted = true;
2403 }
2404 }
2405 if (interrupted) {
2406 filler_ = NULL;
2407 return false;
2408 }
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002409
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002410 SetRootGcRootsReference();
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002411 RootsReferencesExtractor extractor;
2412 heap_->IterateRoots(&extractor, VISIT_ONLY_STRONG);
2413 extractor.SetCollectingAllReferences();
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002414 heap_->IterateRoots(&extractor, VISIT_ALL);
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002415 extractor.FillReferences(this);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002416 filler_ = NULL;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002417 return progress_->ProgressReport(true);
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00002418}
2419
2420
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002421bool V8HeapExplorer::IsEssentialObject(Object* object) {
2422 // We have to use raw_unchecked_* versions because checked versions
2423 // would fail during iteration over object properties.
2424 return object->IsHeapObject()
2425 && !object->IsOddball()
2426 && object != heap_->raw_unchecked_empty_byte_array()
2427 && object != heap_->raw_unchecked_empty_fixed_array()
2428 && object != heap_->raw_unchecked_empty_descriptor_array()
2429 && object != heap_->raw_unchecked_fixed_array_map()
2430 && object != heap_->raw_unchecked_global_property_cell_map()
2431 && object != heap_->raw_unchecked_shared_function_info_map()
2432 && object != heap_->raw_unchecked_free_space_map()
2433 && object != heap_->raw_unchecked_one_pointer_filler_map()
2434 && object != heap_->raw_unchecked_two_pointer_filler_map();
2435}
2436
2437
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002438void V8HeapExplorer::SetClosureReference(HeapObject* parent_obj,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002439 int parent_entry,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002440 String* reference_name,
2441 Object* child_obj) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002442 HeapEntry* child_entry = GetEntry(child_obj);
2443 if (child_entry != NULL) {
2444 filler_->SetNamedReference(HeapGraphEdge::kContextVariable,
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002445 parent_entry,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002446 collection_->names()->GetName(reference_name),
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002447 child_entry);
2448 }
2449}
2450
2451
ulan@chromium.org65a89c22012-02-14 11:46:07 +00002452void V8HeapExplorer::SetNativeBindReference(HeapObject* parent_obj,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002453 int parent_entry,
ulan@chromium.org65a89c22012-02-14 11:46:07 +00002454 const char* reference_name,
2455 Object* child_obj) {
2456 HeapEntry* child_entry = GetEntry(child_obj);
2457 if (child_entry != NULL) {
2458 filler_->SetNamedReference(HeapGraphEdge::kShortcut,
ulan@chromium.org65a89c22012-02-14 11:46:07 +00002459 parent_entry,
2460 reference_name,
ulan@chromium.org65a89c22012-02-14 11:46:07 +00002461 child_entry);
2462 }
2463}
2464
2465
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002466void V8HeapExplorer::SetElementReference(HeapObject* parent_obj,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002467 int parent_entry,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002468 int index,
2469 Object* child_obj) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002470 HeapEntry* child_entry = GetEntry(child_obj);
2471 if (child_entry != NULL) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002472 filler_->SetIndexedReference(HeapGraphEdge::kElement,
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002473 parent_entry,
2474 index,
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002475 child_entry);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002476 }
2477}
2478
2479
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002480void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002481 int parent_entry,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002482 const char* reference_name,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002483 Object* child_obj,
2484 int field_offset) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002485 HeapEntry* child_entry = GetEntry(child_obj);
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002486 if (child_entry == NULL) return;
2487 if (IsEssentialObject(child_obj)) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002488 filler_->SetNamedReference(HeapGraphEdge::kInternal,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002489 parent_entry,
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002490 reference_name,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002491 child_entry);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002492 }
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002493 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002494}
2495
2496
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002497void V8HeapExplorer::SetInternalReference(HeapObject* parent_obj,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002498 int parent_entry,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002499 int index,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002500 Object* child_obj,
2501 int field_offset) {
vegorov@chromium.org42841962010-10-18 11:18:59 +00002502 HeapEntry* child_entry = GetEntry(child_obj);
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002503 if (child_entry == NULL) return;
2504 if (IsEssentialObject(child_obj)) {
vegorov@chromium.org42841962010-10-18 11:18:59 +00002505 filler_->SetNamedReference(HeapGraphEdge::kInternal,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002506 parent_entry,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002507 collection_->names()->GetName(index),
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002508 child_entry);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002509 }
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002510 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002511}
2512
2513
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002514void V8HeapExplorer::SetHiddenReference(HeapObject* parent_obj,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002515 int parent_entry,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002516 int index,
2517 Object* child_obj) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002518 HeapEntry* child_entry = GetEntry(child_obj);
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002519 if (child_entry != NULL && IsEssentialObject(child_obj)) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002520 filler_->SetIndexedReference(HeapGraphEdge::kHidden,
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002521 parent_entry,
2522 index,
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002523 child_entry);
vegorov@chromium.org42841962010-10-18 11:18:59 +00002524 }
2525}
2526
2527
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002528void V8HeapExplorer::SetWeakReference(HeapObject* parent_obj,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002529 int parent_entry,
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002530 int index,
2531 Object* child_obj,
2532 int field_offset) {
2533 HeapEntry* child_entry = GetEntry(child_obj);
2534 if (child_entry != NULL) {
2535 filler_->SetIndexedReference(HeapGraphEdge::kWeak,
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002536 parent_entry,
2537 index,
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002538 child_entry);
2539 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
2540 }
2541}
2542
2543
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002544void V8HeapExplorer::SetPropertyReference(HeapObject* parent_obj,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002545 int parent_entry,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002546 String* reference_name,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002547 Object* child_obj,
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00002548 const char* name_format_string,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002549 int field_offset) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002550 HeapEntry* child_entry = GetEntry(child_obj);
2551 if (child_entry != NULL) {
vegorov@chromium.org42841962010-10-18 11:18:59 +00002552 HeapGraphEdge::Type type = reference_name->length() > 0 ?
2553 HeapGraphEdge::kProperty : HeapGraphEdge::kInternal;
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00002554 const char* name = name_format_string != NULL ?
2555 collection_->names()->GetFormatted(
2556 name_format_string,
2557 *reference_name->ToCString(DISALLOW_NULLS,
2558 ROBUST_STRING_TRAVERSAL)) :
2559 collection_->names()->GetName(reference_name);
2560
vegorov@chromium.org42841962010-10-18 11:18:59 +00002561 filler_->SetNamedReference(type,
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002562 parent_entry,
jkummerow@chromium.org04e4f1e2011-11-14 13:36:17 +00002563 name,
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002564 child_entry);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002565 IndexedReferencesExtractor::MarkVisitedField(parent_obj, field_offset);
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002566 }
2567}
2568
2569
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002570void V8HeapExplorer::SetPropertyShortcutReference(HeapObject* parent_obj,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002571 int parent_entry,
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002572 String* reference_name,
2573 Object* child_obj) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002574 HeapEntry* child_entry = GetEntry(child_obj);
2575 if (child_entry != NULL) {
2576 filler_->SetNamedReference(HeapGraphEdge::kShortcut,
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002577 parent_entry,
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002578 collection_->names()->GetName(reference_name),
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002579 child_entry);
2580 }
2581}
2582
2583
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002584void V8HeapExplorer::SetRootGcRootsReference() {
2585 filler_->SetIndexedAutoIndexReference(
2586 HeapGraphEdge::kElement,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002587 snapshot_->root()->index(),
2588 snapshot_->gc_roots());
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002589}
2590
2591
svenpanne@chromium.orgfb046332012-04-19 12:02:44 +00002592void V8HeapExplorer::SetUserGlobalReference(Object* child_obj) {
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002593 HeapEntry* child_entry = GetEntry(child_obj);
2594 ASSERT(child_entry != NULL);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00002595 filler_->SetNamedAutoIndexReference(
2596 HeapGraphEdge::kShortcut,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002597 snapshot_->root()->index(),
2598 child_entry);
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002599}
2600
2601
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002602void V8HeapExplorer::SetGcRootsReference(VisitorSynchronization::SyncTag tag) {
2603 filler_->SetIndexedAutoIndexReference(
2604 HeapGraphEdge::kElement,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002605 snapshot_->gc_roots()->index(),
2606 snapshot_->gc_subroot(tag));
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00002607}
2608
2609
2610void V8HeapExplorer::SetGcSubrootReference(
2611 VisitorSynchronization::SyncTag tag, bool is_weak, Object* child_obj) {
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002612 HeapEntry* child_entry = GetEntry(child_obj);
2613 if (child_entry != NULL) {
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002614 const char* name = GetStrongGcSubrootName(child_obj);
2615 if (name != NULL) {
2616 filler_->SetNamedReference(
2617 HeapGraphEdge::kInternal,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002618 snapshot_->gc_subroot(tag)->index(),
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002619 name,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002620 child_entry);
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002621 } else {
2622 filler_->SetIndexedAutoIndexReference(
2623 is_weak ? HeapGraphEdge::kWeak : HeapGraphEdge::kElement,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002624 snapshot_->gc_subroot(tag)->index(),
2625 child_entry);
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002626 }
vegorov@chromium.org21b5e952010-11-23 10:24:40 +00002627 }
vegorov@chromium.org26c16f82010-08-11 13:41:03 +00002628}
2629
2630
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002631const char* V8HeapExplorer::GetStrongGcSubrootName(Object* object) {
2632 if (strong_gc_subroot_names_.is_empty()) {
2633#define NAME_ENTRY(name) strong_gc_subroot_names_.SetTag(heap_->name(), #name);
2634#define ROOT_NAME(type, name, camel_name) NAME_ENTRY(name)
2635 STRONG_ROOT_LIST(ROOT_NAME)
2636#undef ROOT_NAME
2637#define STRUCT_MAP_NAME(NAME, Name, name) NAME_ENTRY(name##_map)
2638 STRUCT_LIST(STRUCT_MAP_NAME)
2639#undef STRUCT_MAP_NAME
2640#define SYMBOL_NAME(name, str) NAME_ENTRY(name)
2641 SYMBOL_LIST(SYMBOL_NAME)
2642#undef SYMBOL_NAME
2643#undef NAME_ENTRY
2644 CHECK(!strong_gc_subroot_names_.is_empty());
2645 }
2646 return strong_gc_subroot_names_.GetTag(object);
2647}
2648
2649
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002650void V8HeapExplorer::TagObject(Object* obj, const char* tag) {
mstarzinger@chromium.org88d326b2012-04-23 12:57:22 +00002651 if (IsEssentialObject(obj)) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002652 HeapEntry* entry = GetEntry(obj);
2653 if (entry->name()[0] == '\0') {
2654 entry->set_name(tag);
2655 }
ricow@chromium.org4f693d62011-07-04 14:01:31 +00002656 }
2657}
2658
2659
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002660class GlobalObjectsEnumerator : public ObjectVisitor {
2661 public:
2662 virtual void VisitPointers(Object** start, Object** end) {
2663 for (Object** p = start; p < end; p++) {
2664 if ((*p)->IsGlobalContext()) {
2665 Context* context = Context::cast(*p);
2666 JSObject* proxy = context->global_proxy();
2667 if (proxy->IsJSGlobalProxy()) {
2668 Object* global = proxy->map()->prototype();
2669 if (global->IsJSGlobalObject()) {
2670 objects_.Add(Handle<JSGlobalObject>(JSGlobalObject::cast(global)));
2671 }
2672 }
2673 }
2674 }
2675 }
2676 int count() { return objects_.length(); }
2677 Handle<JSGlobalObject>& at(int i) { return objects_[i]; }
2678
2679 private:
2680 List<Handle<JSGlobalObject> > objects_;
2681};
2682
2683
2684// Modifies heap. Must not be run during heap traversal.
2685void V8HeapExplorer::TagGlobalObjects() {
erikcorry0ad885c2011-11-21 13:51:57 +00002686 HandleScope scope;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002687 Isolate* isolate = Isolate::Current();
2688 GlobalObjectsEnumerator enumerator;
2689 isolate->global_handles()->IterateAllRoots(&enumerator);
2690 Handle<String> document_string =
2691 isolate->factory()->NewStringFromAscii(CStrVector("document"));
2692 Handle<String> url_string =
2693 isolate->factory()->NewStringFromAscii(CStrVector("URL"));
2694 const char** urls = NewArray<const char*>(enumerator.count());
2695 for (int i = 0, l = enumerator.count(); i < l; ++i) {
2696 urls[i] = NULL;
ricow@chromium.org27bf2882011-11-17 08:34:43 +00002697 HandleScope scope;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002698 Handle<JSGlobalObject> global_obj = enumerator.at(i);
2699 Object* obj_document;
2700 if (global_obj->GetProperty(*document_string)->ToObject(&obj_document) &&
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00002701 obj_document->IsJSObject()) {
danno@chromium.orgc31a3c02012-06-15 09:40:41 +00002702 // FixMe: Workaround: SharedWorker's current Isolate has NULL context.
2703 // As result GetProperty(*url_string) will crash.
2704 if (!Isolate::Current()->context() && obj_document->IsJSGlobalProxy())
2705 continue;
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00002706 JSObject* document = JSObject::cast(obj_document);
2707 Object* obj_url;
2708 if (document->GetProperty(*url_string)->ToObject(&obj_url) &&
2709 obj_url->IsString()) {
2710 urls[i] = collection_->names()->GetName(String::cast(obj_url));
2711 }
2712 }
2713 }
2714
2715 AssertNoAllocation no_allocation;
2716 for (int i = 0, l = enumerator.count(); i < l; ++i) {
2717 objects_tags_.SetTag(*enumerator.at(i), urls[i]);
2718 }
2719
2720 DeleteArray(urls);
2721}
2722
2723
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002724class GlobalHandlesExtractor : public ObjectVisitor {
2725 public:
2726 explicit GlobalHandlesExtractor(NativeObjectsExplorer* explorer)
2727 : explorer_(explorer) {}
2728 virtual ~GlobalHandlesExtractor() {}
2729 virtual void VisitPointers(Object** start, Object** end) {
2730 UNREACHABLE();
2731 }
2732 virtual void VisitEmbedderReference(Object** p, uint16_t class_id) {
2733 explorer_->VisitSubtreeWrapper(p, class_id);
2734 }
2735 private:
2736 NativeObjectsExplorer* explorer_;
2737};
2738
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002739
2740class BasicHeapEntriesAllocator : public HeapEntriesAllocator {
2741 public:
2742 BasicHeapEntriesAllocator(
2743 HeapSnapshot* snapshot,
2744 HeapEntry::Type entries_type)
2745 : snapshot_(snapshot),
2746 collection_(snapshot_->collection()),
2747 entries_type_(entries_type) {
2748 }
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002749 virtual HeapEntry* AllocateEntry(HeapThing ptr);
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002750 private:
2751 HeapSnapshot* snapshot_;
2752 HeapSnapshotsCollection* collection_;
2753 HeapEntry::Type entries_type_;
2754};
2755
2756
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002757HeapEntry* BasicHeapEntriesAllocator::AllocateEntry(HeapThing ptr) {
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002758 v8::RetainedObjectInfo* info = reinterpret_cast<v8::RetainedObjectInfo*>(ptr);
2759 intptr_t elements = info->GetElementCount();
2760 intptr_t size = info->GetSizeInBytes();
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002761 const char* name = elements != -1
2762 ? collection_->names()->GetFormatted(
2763 "%s / %" V8_PTR_PREFIX "d entries", info->GetLabel(), elements)
2764 : collection_->names()->GetCopy(info->GetLabel());
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002765 return snapshot_->AddEntry(
2766 entries_type_,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002767 name,
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002768 HeapObjectsMap::GenerateId(info),
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002769 size != -1 ? static_cast<int>(size) : 0);
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002770}
2771
2772
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002773NativeObjectsExplorer::NativeObjectsExplorer(
2774 HeapSnapshot* snapshot, SnapshottingProgressReportingInterface* progress)
2775 : snapshot_(snapshot),
2776 collection_(snapshot_->collection()),
2777 progress_(progress),
2778 embedder_queried_(false),
2779 objects_by_info_(RetainedInfosMatch),
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002780 native_groups_(StringsMatch),
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002781 filler_(NULL) {
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002782 synthetic_entries_allocator_ =
2783 new BasicHeapEntriesAllocator(snapshot, HeapEntry::kSynthetic);
2784 native_entries_allocator_ =
2785 new BasicHeapEntriesAllocator(snapshot, HeapEntry::kNative);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002786}
2787
2788
2789NativeObjectsExplorer::~NativeObjectsExplorer() {
2790 for (HashMap::Entry* p = objects_by_info_.Start();
2791 p != NULL;
2792 p = objects_by_info_.Next(p)) {
2793 v8::RetainedObjectInfo* info =
2794 reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
2795 info->Dispose();
2796 List<HeapObject*>* objects =
2797 reinterpret_cast<List<HeapObject*>* >(p->value);
2798 delete objects;
2799 }
jkummerow@chromium.org1145ef82012-02-02 16:21:15 +00002800 for (HashMap::Entry* p = native_groups_.Start();
2801 p != NULL;
2802 p = native_groups_.Next(p)) {
2803 v8::RetainedObjectInfo* info =
2804 reinterpret_cast<v8::RetainedObjectInfo*>(p->value);
2805 info->Dispose();
2806 }
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002807 delete synthetic_entries_allocator_;
2808 delete native_entries_allocator_;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002809}
2810
2811
2812int NativeObjectsExplorer::EstimateObjectsCount() {
2813 FillRetainedObjects();
2814 return objects_by_info_.occupancy();
2815}
2816
2817
2818void NativeObjectsExplorer::FillRetainedObjects() {
2819 if (embedder_queried_) return;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002820 Isolate* isolate = Isolate::Current();
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002821 // Record objects that are joined into ObjectGroups.
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002822 isolate->heap()->CallGlobalGCPrologueCallback();
2823 List<ObjectGroup*>* groups = isolate->global_handles()->object_groups();
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002824 for (int i = 0; i < groups->length(); ++i) {
2825 ObjectGroup* group = groups->at(i);
2826 if (group->info_ == NULL) continue;
2827 List<HeapObject*>* list = GetListMaybeDisposeInfo(group->info_);
karlklose@chromium.org44bc7082011-04-11 12:33:05 +00002828 for (size_t j = 0; j < group->length_; ++j) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002829 HeapObject* obj = HeapObject::cast(*group->objects_[j]);
2830 list->Add(obj);
2831 in_groups_.Insert(obj);
2832 }
2833 group->info_ = NULL; // Acquire info object ownership.
2834 }
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002835 isolate->global_handles()->RemoveObjectGroups();
2836 isolate->heap()->CallGlobalGCEpilogueCallback();
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002837 // Record objects that are not in ObjectGroups, but have class ID.
2838 GlobalHandlesExtractor extractor(this);
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002839 isolate->global_handles()->IterateAllRootsWithClassIds(&extractor);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002840 embedder_queried_ = true;
2841}
2842
rossberg@chromium.org994edf62012-02-06 10:12:55 +00002843void NativeObjectsExplorer::FillImplicitReferences() {
2844 Isolate* isolate = Isolate::Current();
2845 List<ImplicitRefGroup*>* groups =
2846 isolate->global_handles()->implicit_ref_groups();
2847 for (int i = 0; i < groups->length(); ++i) {
2848 ImplicitRefGroup* group = groups->at(i);
2849 HeapObject* parent = *group->parent_;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002850 int parent_entry =
2851 filler_->FindOrAddEntry(parent, native_entries_allocator_)->index();
2852 ASSERT(parent_entry != HeapEntry::kNoEntry);
rossberg@chromium.org994edf62012-02-06 10:12:55 +00002853 Object*** children = group->children_;
2854 for (size_t j = 0; j < group->length_; ++j) {
2855 Object* child = *children[j];
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002856 HeapEntry* child_entry =
2857 filler_->FindOrAddEntry(child, native_entries_allocator_);
rossberg@chromium.org994edf62012-02-06 10:12:55 +00002858 filler_->SetNamedReference(
2859 HeapGraphEdge::kInternal,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002860 parent_entry,
rossberg@chromium.org994edf62012-02-06 10:12:55 +00002861 "native",
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002862 child_entry);
rossberg@chromium.org994edf62012-02-06 10:12:55 +00002863 }
2864 }
2865}
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002866
2867List<HeapObject*>* NativeObjectsExplorer::GetListMaybeDisposeInfo(
2868 v8::RetainedObjectInfo* info) {
2869 HashMap::Entry* entry =
2870 objects_by_info_.Lookup(info, InfoHash(info), true);
2871 if (entry->value != NULL) {
2872 info->Dispose();
2873 } else {
2874 entry->value = new List<HeapObject*>(4);
2875 }
2876 return reinterpret_cast<List<HeapObject*>* >(entry->value);
2877}
2878
2879
2880bool NativeObjectsExplorer::IterateAndExtractReferences(
2881 SnapshotFillerInterface* filler) {
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002882 filler_ = filler;
2883 FillRetainedObjects();
rossberg@chromium.org994edf62012-02-06 10:12:55 +00002884 FillImplicitReferences();
2885 if (EstimateObjectsCount() > 0) {
2886 for (HashMap::Entry* p = objects_by_info_.Start();
2887 p != NULL;
2888 p = objects_by_info_.Next(p)) {
2889 v8::RetainedObjectInfo* info =
2890 reinterpret_cast<v8::RetainedObjectInfo*>(p->key);
2891 SetNativeRootReference(info);
2892 List<HeapObject*>* objects =
2893 reinterpret_cast<List<HeapObject*>* >(p->value);
2894 for (int i = 0; i < objects->length(); ++i) {
2895 SetWrapperNativeReferences(objects->at(i), info);
2896 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002897 }
rossberg@chromium.org994edf62012-02-06 10:12:55 +00002898 SetRootNativeRootsReference();
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002899 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002900 filler_ = NULL;
2901 return true;
2902}
2903
2904
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002905class NativeGroupRetainedObjectInfo : public v8::RetainedObjectInfo {
2906 public:
2907 explicit NativeGroupRetainedObjectInfo(const char* label)
2908 : disposed_(false),
2909 hash_(reinterpret_cast<intptr_t>(label)),
2910 label_(label) {
2911 }
2912
2913 virtual ~NativeGroupRetainedObjectInfo() {}
2914 virtual void Dispose() {
2915 CHECK(!disposed_);
2916 disposed_ = true;
jkummerow@chromium.org1145ef82012-02-02 16:21:15 +00002917 delete this;
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002918 }
2919 virtual bool IsEquivalent(RetainedObjectInfo* other) {
2920 return hash_ == other->GetHash() && !strcmp(label_, other->GetLabel());
2921 }
2922 virtual intptr_t GetHash() { return hash_; }
2923 virtual const char* GetLabel() { return label_; }
2924
2925 private:
2926 bool disposed_;
2927 intptr_t hash_;
2928 const char* label_;
2929};
2930
2931
2932NativeGroupRetainedObjectInfo* NativeObjectsExplorer::FindOrAddGroupInfo(
2933 const char* label) {
2934 const char* label_copy = collection_->names()->GetCopy(label);
2935 uint32_t hash = HashSequentialString(label_copy,
2936 static_cast<int>(strlen(label_copy)),
2937 HEAP->HashSeed());
2938 HashMap::Entry* entry = native_groups_.Lookup(const_cast<char*>(label_copy),
2939 hash, true);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002940 if (entry->value == NULL) {
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002941 entry->value = new NativeGroupRetainedObjectInfo(label);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002942 }
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002943 return static_cast<NativeGroupRetainedObjectInfo*>(entry->value);
2944}
2945
2946
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002947void NativeObjectsExplorer::SetNativeRootReference(
2948 v8::RetainedObjectInfo* info) {
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002949 HeapEntry* child_entry =
2950 filler_->FindOrAddEntry(info, native_entries_allocator_);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002951 ASSERT(child_entry != NULL);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002952 NativeGroupRetainedObjectInfo* group_info =
2953 FindOrAddGroupInfo(info->GetGroupLabel());
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002954 HeapEntry* group_entry =
2955 filler_->FindOrAddEntry(group_info, synthetic_entries_allocator_);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002956 filler_->SetNamedAutoIndexReference(
2957 HeapGraphEdge::kInternal,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002958 group_entry->index(),
2959 child_entry);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002960}
2961
2962
2963void NativeObjectsExplorer::SetWrapperNativeReferences(
2964 HeapObject* wrapper, v8::RetainedObjectInfo* info) {
2965 HeapEntry* wrapper_entry = filler_->FindEntry(wrapper);
2966 ASSERT(wrapper_entry != NULL);
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002967 HeapEntry* info_entry =
2968 filler_->FindOrAddEntry(info, native_entries_allocator_);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002969 ASSERT(info_entry != NULL);
2970 filler_->SetNamedReference(HeapGraphEdge::kInternal,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002971 wrapper_entry->index(),
kmillikin@chromium.orgc36ce6e2011-04-04 08:25:31 +00002972 "native",
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002973 info_entry);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002974 filler_->SetIndexedAutoIndexReference(HeapGraphEdge::kElement,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002975 info_entry->index(),
2976 wrapper_entry);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002977}
2978
2979
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002980void NativeObjectsExplorer::SetRootNativeRootsReference() {
2981 for (HashMap::Entry* entry = native_groups_.Start();
2982 entry;
2983 entry = native_groups_.Next(entry)) {
2984 NativeGroupRetainedObjectInfo* group_info =
2985 static_cast<NativeGroupRetainedObjectInfo*>(entry->value);
jkummerow@chromium.orgab7dad42012-02-07 12:07:34 +00002986 HeapEntry* group_entry =
2987 filler_->FindOrAddEntry(group_info, native_entries_allocator_);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002988 ASSERT(group_entry != NULL);
2989 filler_->SetIndexedAutoIndexReference(
2990 HeapGraphEdge::kElement,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00002991 snapshot_->root()->index(),
2992 group_entry);
danno@chromium.orgfa458e42012-02-01 10:48:36 +00002993 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00002994}
2995
2996
2997void NativeObjectsExplorer::VisitSubtreeWrapper(Object** p, uint16_t class_id) {
2998 if (in_groups_.Contains(*p)) return;
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00002999 Isolate* isolate = Isolate::Current();
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00003000 v8::RetainedObjectInfo* info =
sgjesse@chromium.orgea88ce92011-03-23 11:19:56 +00003001 isolate->heap_profiler()->ExecuteWrapperClassCallback(class_id, p);
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00003002 if (info == NULL) return;
3003 GetListMaybeDisposeInfo(info)->Add(HeapObject::cast(*p));
3004}
3005
3006
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003007class SnapshotFiller : public SnapshotFillerInterface {
3008 public:
3009 explicit SnapshotFiller(HeapSnapshot* snapshot, HeapEntriesMap* entries)
3010 : snapshot_(snapshot),
3011 collection_(snapshot->collection()),
3012 entries_(entries) { }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00003013 HeapEntry* AddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003014 HeapEntry* entry = allocator->AllocateEntry(ptr);
3015 entries_->Pair(ptr, entry->index());
3016 return entry;
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003017 }
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00003018 HeapEntry* FindEntry(HeapThing ptr) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003019 int index = entries_->Map(ptr);
3020 return index != HeapEntry::kNoEntry ? &snapshot_->entries()[index] : NULL;
whesse@chromium.orgb08986c2011-03-14 16:13:42 +00003021 }
3022 HeapEntry* FindOrAddEntry(HeapThing ptr, HeapEntriesAllocator* allocator) {
3023 HeapEntry* entry = FindEntry(ptr);
3024 return entry != NULL ? entry : AddEntry(ptr, allocator);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003025 }
3026 void SetIndexedReference(HeapGraphEdge::Type type,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003027 int parent,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003028 int index,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003029 HeapEntry* child_entry) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003030 HeapEntry* parent_entry = &snapshot_->entries()[parent];
3031 parent_entry->SetIndexedReference(type, index, child_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003032 }
3033 void SetIndexedAutoIndexReference(HeapGraphEdge::Type type,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003034 int parent,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003035 HeapEntry* child_entry) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003036 HeapEntry* parent_entry = &snapshot_->entries()[parent];
3037 int index = parent_entry->children_count() + 1;
3038 parent_entry->SetIndexedReference(type, index, child_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003039 }
3040 void SetNamedReference(HeapGraphEdge::Type type,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003041 int parent,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003042 const char* reference_name,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003043 HeapEntry* child_entry) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003044 HeapEntry* parent_entry = &snapshot_->entries()[parent];
3045 parent_entry->SetNamedReference(type, reference_name, child_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003046 }
3047 void SetNamedAutoIndexReference(HeapGraphEdge::Type type,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003048 int parent,
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003049 HeapEntry* child_entry) {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003050 HeapEntry* parent_entry = &snapshot_->entries()[parent];
3051 int index = parent_entry->children_count() + 1;
3052 parent_entry->SetNamedReference(
3053 type,
3054 collection_->names()->GetName(index),
3055 child_entry);
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003056 }
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003057
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003058 private:
3059 HeapSnapshot* snapshot_;
3060 HeapSnapshotsCollection* collection_;
3061 HeapEntriesMap* entries_;
3062};
3063
3064
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00003065HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot,
3066 v8::ActivityControl* control)
3067 : snapshot_(snapshot),
3068 control_(control),
3069 v8_heap_explorer_(snapshot_, this),
3070 dom_explorer_(snapshot_, this) {
3071}
3072
3073
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003074bool HeapSnapshotGenerator::GenerateSnapshot() {
ricow@chromium.orgd2be9012011-06-01 06:00:58 +00003075 v8_heap_explorer_.TagGlobalObjects();
3076
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003077 // TODO(1562) Profiler assumes that any object that is in the heap after
3078 // full GC is reachable from the root when computing dominators.
3079 // This is not true for weakly reachable objects.
3080 // As a temporary solution we call GC twice.
rossberg@chromium.org994edf62012-02-06 10:12:55 +00003081 Isolate::Current()->heap()->CollectAllGarbage(
3082 Heap::kMakeHeapIterableMask,
3083 "HeapSnapshotGenerator::GenerateSnapshot");
3084 Isolate::Current()->heap()->CollectAllGarbage(
3085 Heap::kMakeHeapIterableMask,
3086 "HeapSnapshotGenerator::GenerateSnapshot");
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003087
3088#ifdef DEBUG
3089 Heap* debug_heap = Isolate::Current()->heap();
3090 ASSERT(!debug_heap->old_data_space()->was_swept_conservatively());
3091 ASSERT(!debug_heap->old_pointer_space()->was_swept_conservatively());
3092 ASSERT(!debug_heap->code_space()->was_swept_conservatively());
3093 ASSERT(!debug_heap->cell_space()->was_swept_conservatively());
3094 ASSERT(!debug_heap->map_space()->was_swept_conservatively());
3095#endif
3096
3097 // The following code uses heap iterators, so we want the heap to be
3098 // stable. It should follow TagGlobalObjects as that can allocate.
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003099 AssertNoAllocation no_alloc;
3100
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003101#ifdef DEBUG
3102 debug_heap->Verify();
3103#endif
3104
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003105 SetProgressTotal(1); // 1 pass.
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003106
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003107#ifdef DEBUG
3108 debug_heap->Verify();
3109#endif
3110
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003111 if (!FillReferences()) return false;
3112
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003113 snapshot_->FillChildren();
jkummerow@chromium.org1456e702012-03-30 08:38:13 +00003114 snapshot_->RememberLastJSObjectId();
3115
ager@chromium.org9ee27ae2011-03-02 13:43:26 +00003116 progress_counter_ = progress_total_;
3117 if (!ProgressReport(true)) return false;
3118 return true;
3119}
3120
3121
3122void HeapSnapshotGenerator::ProgressStep() {
3123 ++progress_counter_;
3124}
3125
3126
3127bool HeapSnapshotGenerator::ProgressReport(bool force) {
3128 const int kProgressReportGranularity = 10000;
3129 if (control_ != NULL
3130 && (force || progress_counter_ % kProgressReportGranularity == 0)) {
3131 return
3132 control_->ReportProgressValue(progress_counter_, progress_total_) ==
3133 v8::ActivityControl::kContinue;
3134 }
3135 return true;
3136}
3137
3138
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003139void HeapSnapshotGenerator::SetProgressTotal(int iterations_count) {
3140 if (control_ == NULL) return;
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003141 HeapIterator iterator(HeapIterator::kFilterUnreachable);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003142 progress_total_ = iterations_count * (
erik.corry@gmail.comc3b670f2011-10-05 21:44:48 +00003143 v8_heap_explorer_.EstimateObjectsCount(&iterator) +
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003144 dom_explorer_.EstimateObjectsCount());
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003145 progress_counter_ = 0;
3146}
3147
3148
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003149bool HeapSnapshotGenerator::FillReferences() {
3150 SnapshotFiller filler(snapshot_, &entries_);
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003151 v8_heap_explorer_.AddRootEntries(&filler);
ulan@chromium.org9a21ec42012-03-06 08:42:24 +00003152 return v8_heap_explorer_.IterateAndExtractReferences(&filler)
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003153 && dom_explorer_.IterateAndExtractReferences(&filler);
ager@chromium.org5f0c45f2010-12-17 08:51:21 +00003154}
3155
3156
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003157template<int bytes> struct MaxDecimalDigitsIn;
3158template<> struct MaxDecimalDigitsIn<4> {
3159 static const int kSigned = 11;
3160 static const int kUnsigned = 10;
3161};
3162template<> struct MaxDecimalDigitsIn<8> {
3163 static const int kSigned = 20;
3164 static const int kUnsigned = 20;
3165};
3166
3167
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003168class OutputStreamWriter {
3169 public:
3170 explicit OutputStreamWriter(v8::OutputStream* stream)
3171 : stream_(stream),
3172 chunk_size_(stream->GetChunkSize()),
3173 chunk_(chunk_size_),
3174 chunk_pos_(0),
3175 aborted_(false) {
3176 ASSERT(chunk_size_ > 0);
3177 }
3178 bool aborted() { return aborted_; }
3179 void AddCharacter(char c) {
3180 ASSERT(c != '\0');
3181 ASSERT(chunk_pos_ < chunk_size_);
3182 chunk_[chunk_pos_++] = c;
3183 MaybeWriteChunk();
3184 }
3185 void AddString(const char* s) {
3186 AddSubstring(s, StrLength(s));
3187 }
3188 void AddSubstring(const char* s, int n) {
3189 if (n <= 0) return;
3190 ASSERT(static_cast<size_t>(n) <= strlen(s));
3191 const char* s_end = s + n;
3192 while (s < s_end) {
3193 int s_chunk_size = Min(
3194 chunk_size_ - chunk_pos_, static_cast<int>(s_end - s));
3195 ASSERT(s_chunk_size > 0);
3196 memcpy(chunk_.start() + chunk_pos_, s, s_chunk_size);
3197 s += s_chunk_size;
3198 chunk_pos_ += s_chunk_size;
3199 MaybeWriteChunk();
3200 }
3201 }
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003202 void AddNumber(unsigned n) { AddNumberImpl<unsigned>(n, "%u"); }
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003203 void Finalize() {
3204 if (aborted_) return;
3205 ASSERT(chunk_pos_ < chunk_size_);
3206 if (chunk_pos_ != 0) {
3207 WriteChunk();
3208 }
3209 stream_->EndOfStream();
3210 }
3211
3212 private:
3213 template<typename T>
3214 void AddNumberImpl(T n, const char* format) {
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003215 // Buffer for the longest value plus trailing \0
3216 static const int kMaxNumberSize =
3217 MaxDecimalDigitsIn<sizeof(T)>::kUnsigned + 1;
3218 if (chunk_size_ - chunk_pos_ >= kMaxNumberSize) {
3219 int result = OS::SNPrintF(
3220 chunk_.SubVector(chunk_pos_, chunk_size_), format, n);
3221 ASSERT(result != -1);
3222 chunk_pos_ += result;
3223 MaybeWriteChunk();
3224 } else {
3225 EmbeddedVector<char, kMaxNumberSize> buffer;
3226 int result = OS::SNPrintF(buffer, format, n);
3227 USE(result);
3228 ASSERT(result != -1);
3229 AddString(buffer.start());
3230 }
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003231 }
3232 void MaybeWriteChunk() {
3233 ASSERT(chunk_pos_ <= chunk_size_);
3234 if (chunk_pos_ == chunk_size_) {
3235 WriteChunk();
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003236 }
3237 }
3238 void WriteChunk() {
3239 if (aborted_) return;
3240 if (stream_->WriteAsciiChunk(chunk_.start(), chunk_pos_) ==
3241 v8::OutputStream::kAbort) aborted_ = true;
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003242 chunk_pos_ = 0;
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003243 }
3244
3245 v8::OutputStream* stream_;
3246 int chunk_size_;
3247 ScopedVector<char> chunk_;
3248 int chunk_pos_;
3249 bool aborted_;
3250};
3251
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003252
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003253// type, name|index, to_node.
3254const int HeapSnapshotJSONSerializer::kEdgeFieldsCount = 3;
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003255// type, name, id, self_size, children_index.
3256const int HeapSnapshotJSONSerializer::kNodeFieldsCount = 5;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003257
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003258void HeapSnapshotJSONSerializer::Serialize(v8::OutputStream* stream) {
3259 ASSERT(writer_ == NULL);
3260 writer_ = new OutputStreamWriter(stream);
3261
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003262 HeapSnapshot* original_snapshot = NULL;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003263 if (snapshot_->RawSnapshotSize() >=
ulan@chromium.org65a89c22012-02-14 11:46:07 +00003264 SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize) {
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003265 // The snapshot is too big. Serialize a fake snapshot.
3266 original_snapshot = snapshot_;
3267 snapshot_ = CreateFakeSnapshot();
3268 }
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003269
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003270 SerializeImpl();
3271
3272 delete writer_;
3273 writer_ = NULL;
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003274
3275 if (original_snapshot != NULL) {
3276 delete snapshot_;
3277 snapshot_ = original_snapshot;
3278 }
3279}
3280
3281
3282HeapSnapshot* HeapSnapshotJSONSerializer::CreateFakeSnapshot() {
3283 HeapSnapshot* result = new HeapSnapshot(snapshot_->collection(),
3284 HeapSnapshot::kFull,
3285 snapshot_->title(),
3286 snapshot_->uid());
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003287 result->AddRootEntry();
ulan@chromium.org65a89c22012-02-14 11:46:07 +00003288 const char* text = snapshot_->collection()->names()->GetFormatted(
3289 "The snapshot is too big. "
svenpanne@chromium.org4efbdb12012-03-12 08:18:42 +00003290 "Maximum snapshot size is %" V8_PTR_PREFIX "u MB. "
3291 "Actual snapshot size is %" V8_PTR_PREFIX "u MB.",
ulan@chromium.org65a89c22012-02-14 11:46:07 +00003292 SnapshotSizeConstants<kPointerSize>::kMaxSerializableSnapshotRawSize / MB,
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003293 (snapshot_->RawSnapshotSize() + MB - 1) / MB);
3294 HeapEntry* message = result->AddEntry(HeapEntry::kString, text, 0, 4);
3295 result->root()->SetIndexedReference(HeapGraphEdge::kElement, 1, message);
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003296 result->FillChildren();
fschneider@chromium.org1805e212011-09-05 10:49:12 +00003297 return result;
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003298}
3299
3300
3301void HeapSnapshotJSONSerializer::SerializeImpl() {
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003302 ASSERT(0 == snapshot_->root()->index());
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003303 writer_->AddCharacter('{');
3304 writer_->AddString("\"snapshot\":{");
3305 SerializeSnapshot();
3306 if (writer_->aborted()) return;
3307 writer_->AddString("},\n");
3308 writer_->AddString("\"nodes\":[");
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003309 SerializeNodes();
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003310 if (writer_->aborted()) return;
3311 writer_->AddString("],\n");
3312 writer_->AddString("\"edges\":[");
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003313 SerializeEdges();
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003314 if (writer_->aborted()) return;
3315 writer_->AddString("],\n");
3316 writer_->AddString("\"strings\":[");
3317 SerializeStrings();
3318 if (writer_->aborted()) return;
3319 writer_->AddCharacter(']');
3320 writer_->AddCharacter('}');
3321 writer_->Finalize();
3322}
3323
3324
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003325int HeapSnapshotJSONSerializer::GetStringId(const char* s) {
3326 HashMap::Entry* cache_entry = strings_.Lookup(
3327 const_cast<char*>(s), ObjectHash(s), true);
3328 if (cache_entry->value == NULL) {
3329 cache_entry->value = reinterpret_cast<void*>(next_string_id_++);
3330 }
3331 return static_cast<int>(reinterpret_cast<intptr_t>(cache_entry->value));
3332}
3333
3334
yangguo@chromium.orgcb9affa2012-05-15 12:16:38 +00003335static int utoa(unsigned value, const Vector<char>& buffer, int buffer_pos) {
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003336 int number_of_digits = 0;
yangguo@chromium.orgcb9affa2012-05-15 12:16:38 +00003337 unsigned t = value;
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003338 do {
3339 ++number_of_digits;
3340 } while (t /= 10);
3341
3342 buffer_pos += number_of_digits;
3343 int result = buffer_pos;
3344 do {
3345 int last_digit = value % 10;
3346 buffer[--buffer_pos] = '0' + last_digit;
3347 value /= 10;
3348 } while (value);
3349 return result;
3350}
3351
3352
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003353void HeapSnapshotJSONSerializer::SerializeEdge(HeapGraphEdge* edge,
3354 bool first_edge) {
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003355 // The buffer needs space for 3 unsigned ints, 3 commas and \0
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003356 static const int kBufferSize =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003357 MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned * 3 + 3 + 1; // NOLINT
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003358 EmbeddedVector<char, kBufferSize> buffer;
3359 int edge_name_or_index = edge->type() == HeapGraphEdge::kElement
ricow@chromium.org64e3a4b2011-12-13 08:07:27 +00003360 || edge->type() == HeapGraphEdge::kHidden
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003361 || edge->type() == HeapGraphEdge::kWeak
3362 ? edge->index() : GetStringId(edge->name());
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003363 int buffer_pos = 0;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003364 if (!first_edge) {
3365 buffer[buffer_pos++] = ',';
3366 }
yangguo@chromium.orgcb9affa2012-05-15 12:16:38 +00003367 buffer_pos = utoa(edge->type(), buffer, buffer_pos);
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003368 buffer[buffer_pos++] = ',';
yangguo@chromium.orgcb9affa2012-05-15 12:16:38 +00003369 buffer_pos = utoa(edge_name_or_index, buffer, buffer_pos);
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003370 buffer[buffer_pos++] = ',';
yangguo@chromium.orgcb9affa2012-05-15 12:16:38 +00003371 buffer_pos = utoa(entry_index(edge->to()), buffer, buffer_pos);
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003372 buffer[buffer_pos++] = '\0';
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003373 writer_->AddString(buffer.start());
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003374}
3375
3376
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003377void HeapSnapshotJSONSerializer::SerializeEdges() {
3378 List<HeapGraphEdge*>& edges = snapshot_->children();
3379 for (int i = 0; i < edges.length(); ++i) {
3380 ASSERT(i == 0 ||
3381 edges[i - 1]->from()->index() <= edges[i]->from()->index());
3382 SerializeEdge(edges[i], i == 0);
3383 if (writer_->aborted()) return;
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003384 }
3385}
3386
3387
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003388void HeapSnapshotJSONSerializer::SerializeNode(HeapEntry* entry) {
3389 // The buffer needs space for 5 unsigned ints, 5 commas, \n and \0
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003390 static const int kBufferSize =
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003391 5 * MaxDecimalDigitsIn<sizeof(unsigned)>::kUnsigned // NOLINT
mstarzinger@chromium.org15613d02012-05-23 12:04:37 +00003392 + 5 + 1 + 1;
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003393 EmbeddedVector<char, kBufferSize> buffer;
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003394 int buffer_pos = 0;
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003395 if (entry_index(entry) != 0) {
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003396 buffer[buffer_pos++] = ',';
3397 }
yangguo@chromium.orgcb9affa2012-05-15 12:16:38 +00003398 buffer_pos = utoa(entry->type(), buffer, buffer_pos);
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003399 buffer[buffer_pos++] = ',';
yangguo@chromium.orgcb9affa2012-05-15 12:16:38 +00003400 buffer_pos = utoa(GetStringId(entry->name()), buffer, buffer_pos);
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003401 buffer[buffer_pos++] = ',';
yangguo@chromium.orgcb9affa2012-05-15 12:16:38 +00003402 buffer_pos = utoa(entry->id(), buffer, buffer_pos);
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003403 buffer[buffer_pos++] = ',';
yangguo@chromium.orgcb9affa2012-05-15 12:16:38 +00003404 buffer_pos = utoa(entry->self_size(), buffer, buffer_pos);
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003405 buffer[buffer_pos++] = ',';
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003406 buffer_pos = utoa(entry->children_count(), buffer, buffer_pos);
yangguo@chromium.orgcb9affa2012-05-15 12:16:38 +00003407 buffer[buffer_pos++] = '\n';
fschneider@chromium.org7d10be52012-04-10 12:30:14 +00003408 buffer[buffer_pos++] = '\0';
kmillikin@chromium.orgbe6bd102012-02-23 08:45:21 +00003409 writer_->AddString(buffer.start());
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003410}
3411
3412
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003413void HeapSnapshotJSONSerializer::SerializeNodes() {
3414 List<HeapEntry>& entries = snapshot_->entries();
3415 for (int i = 0; i < entries.length(); ++i) {
3416 SerializeNode(&entries[i]);
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003417 if (writer_->aborted()) return;
3418 }
3419}
3420
3421
3422void HeapSnapshotJSONSerializer::SerializeSnapshot() {
3423 writer_->AddString("\"title\":\"");
3424 writer_->AddString(snapshot_->title());
3425 writer_->AddString("\"");
3426 writer_->AddString(",\"uid\":");
3427 writer_->AddNumber(snapshot_->uid());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003428 writer_->AddString(",\"meta\":");
3429 // The object describing node serialization layout.
3430 // We use a set of macros to improve readability.
ulan@chromium.org0e3f88b2012-05-22 09:16:05 +00003431#define JSON_A(s) "[" s "]"
3432#define JSON_O(s) "{" s "}"
3433#define JSON_S(s) "\"" s "\""
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003434 writer_->AddString(JSON_O(
3435 JSON_S("node_fields") ":" JSON_A(
3436 JSON_S("type") ","
3437 JSON_S("name") ","
3438 JSON_S("id") ","
3439 JSON_S("self_size") ","
mmassi@chromium.org7028c052012-06-13 11:51:58 +00003440 JSON_S("edge_count")) ","
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003441 JSON_S("node_types") ":" JSON_A(
3442 JSON_A(
3443 JSON_S("hidden") ","
3444 JSON_S("array") ","
3445 JSON_S("string") ","
3446 JSON_S("object") ","
3447 JSON_S("code") ","
3448 JSON_S("closure") ","
3449 JSON_S("regexp") ","
3450 JSON_S("number") ","
3451 JSON_S("native") ","
3452 JSON_S("synthetic")) ","
3453 JSON_S("string") ","
3454 JSON_S("number") ","
3455 JSON_S("number") ","
3456 JSON_S("number") ","
3457 JSON_S("number") ","
3458 JSON_S("number")) ","
3459 JSON_S("edge_fields") ":" JSON_A(
3460 JSON_S("type") ","
3461 JSON_S("name_or_index") ","
3462 JSON_S("to_node")) ","
3463 JSON_S("edge_types") ":" JSON_A(
3464 JSON_A(
3465 JSON_S("context") ","
3466 JSON_S("element") ","
3467 JSON_S("property") ","
3468 JSON_S("internal") ","
3469 JSON_S("hidden") ","
3470 JSON_S("shortcut") ","
3471 JSON_S("weak")) ","
3472 JSON_S("string_or_number") ","
3473 JSON_S("node"))));
3474#undef JSON_S
3475#undef JSON_O
3476#undef JSON_A
3477 writer_->AddString(",\"node_count\":");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003478 writer_->AddNumber(snapshot_->entries().length());
erik.corry@gmail.comed49e962012-04-17 11:57:53 +00003479 writer_->AddString(",\"edge_count\":");
jkummerow@chromium.org212d9642012-05-11 15:02:09 +00003480 writer_->AddNumber(snapshot_->edges().length());
erik.corry@gmail.comd88afa22010-09-15 12:33:05 +00003481}
3482
3483
3484static void WriteUChar(OutputStreamWriter* w, unibrow::uchar u) {
3485 static const char hex_chars[] = "0123456789ABCDEF";
3486 w->AddString("\\u");
3487 w->AddCharacter(hex_chars[(u >> 12) & 0xf]);
3488 w->AddCharacter(hex_chars[(u >> 8) & 0xf]);
3489 w->AddCharacter(hex_chars[(u >> 4) & 0xf]);
3490 w->AddCharacter(hex_chars[u & 0xf]);
3491}
3492
3493void HeapSnapshotJSONSerializer::SerializeString(const unsigned char* s) {
3494 writer_->AddCharacter('\n');
3495 writer_->AddCharacter('\"');
3496 for ( ; *s != '\0'; ++s) {
3497 switch (*s) {
3498 case '\b':
3499 writer_->AddString("\\b");
3500 continue;
3501 case '\f':
3502 writer_->AddString("\\f");
3503 continue;
3504 case '\n':
3505 writer_->AddString("\\n");
3506 continue;
3507 case '\r':
3508 writer_->AddString("\\r");
3509 continue;
3510 case '\t':
3511 writer_->AddString("\\t");
3512 continue;
3513 case '\"':
3514 case '\\':
3515 writer_->AddCharacter('\\');
3516 writer_->AddCharacter(*s);
3517 continue;
3518 default:
3519 if (*s > 31 && *s < 128) {
3520 writer_->AddCharacter(*s);
3521 } else if (*s <= 31) {
3522 // Special character with no dedicated literal.
3523 WriteUChar(writer_, *s);
3524 } else {
3525 // Convert UTF-8 into \u UTF-16 literal.
3526 unsigned length = 1, cursor = 0;
3527 for ( ; length <= 4 && *(s + length) != '\0'; ++length) { }
3528 unibrow::uchar c = unibrow::Utf8::CalculateValue(s, length, &cursor);
3529 if (c != unibrow::Utf8::kBadChar) {
3530 WriteUChar(writer_, c);
3531 ASSERT(cursor != 0);
3532 s += cursor - 1;
3533 } else {
3534 writer_->AddCharacter('?');
3535 }
3536 }
3537 }
3538 }
3539 writer_->AddCharacter('\"');
3540}
3541
3542
3543void HeapSnapshotJSONSerializer::SerializeStrings() {
3544 List<HashMap::Entry*> sorted_strings;
3545 SortHashMap(&strings_, &sorted_strings);
3546 writer_->AddString("\"<dummy>\"");
3547 for (int i = 0; i < sorted_strings.length(); ++i) {
3548 writer_->AddCharacter(',');
3549 SerializeString(
3550 reinterpret_cast<const unsigned char*>(sorted_strings[i]->key));
3551 if (writer_->aborted()) return;
3552 }
3553}
3554
3555
3556template<typename T>
3557inline static int SortUsingEntryValue(const T* x, const T* y) {
3558 uintptr_t x_uint = reinterpret_cast<uintptr_t>((*x)->value);
3559 uintptr_t y_uint = reinterpret_cast<uintptr_t>((*y)->value);
3560 if (x_uint > y_uint) {
3561 return 1;
3562 } else if (x_uint == y_uint) {
3563 return 0;
3564 } else {
3565 return -1;
3566 }
3567}
3568
3569
3570void HeapSnapshotJSONSerializer::SortHashMap(
3571 HashMap* map, List<HashMap::Entry*>* sorted_entries) {
3572 for (HashMap::Entry* p = map->Start(); p != NULL; p = map->Next(p))
3573 sorted_entries->Add(p);
3574 sorted_entries->Sort(SortUsingEntryValue);
3575}
3576
fschneider@chromium.org086aac62010-03-17 13:18:24 +00003577} } // namespace v8::internal