blob: 7054b125950863776f41a5555d8aeaf807bb5a83 [file] [log] [blame]
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001// Copyright 2010 the V8 project authors. All rights reserved.
2// 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
ricow@chromium.orgc9c80822010-04-21 08:22:37 +000028#ifdef ENABLE_LOGGING_AND_PROFILING
lrn@chromium.org25156de2010-04-06 13:10:27 +000029
fschneider@chromium.org086aac62010-03-17 13:18:24 +000030#include "v8.h"
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000031#include "global-handles.h"
ager@chromium.org2cc82ae2010-06-14 07:35:38 +000032#include "scopeinfo.h"
33#include "top.h"
34#include "zone-inl.h"
fschneider@chromium.org086aac62010-03-17 13:18:24 +000035
36#include "profile-generator-inl.h"
37
fschneider@chromium.org086aac62010-03-17 13:18:24 +000038namespace v8 {
39namespace internal {
40
41
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000042TokenEnumerator::TokenEnumerator()
43 : token_locations_(4),
44 token_removed_(4) {
45}
46
47
48TokenEnumerator::~TokenEnumerator() {
49 for (int i = 0; i < token_locations_.length(); ++i) {
50 if (!token_removed_[i]) {
51 GlobalHandles::ClearWeakness(token_locations_[i]);
52 GlobalHandles::Destroy(token_locations_[i]);
53 }
54 }
55}
56
57
58int TokenEnumerator::GetTokenId(Object* token) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +000059 if (token == NULL) return TokenEnumerator::kNoSecurityToken;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000060 for (int i = 0; i < token_locations_.length(); ++i) {
61 if (*token_locations_[i] == token && !token_removed_[i]) return i;
62 }
63 Handle<Object> handle = GlobalHandles::Create(token);
64 // handle.location() points to a memory cell holding a pointer
65 // to a token object in the V8's heap.
66 GlobalHandles::MakeWeak(handle.location(), this, TokenRemovedCallback);
67 token_locations_.Add(handle.location());
68 token_removed_.Add(false);
69 return token_locations_.length() - 1;
70}
71
72
73void TokenEnumerator::TokenRemovedCallback(v8::Persistent<v8::Value> handle,
74 void* parameter) {
75 reinterpret_cast<TokenEnumerator*>(parameter)->TokenRemoved(
76 Utils::OpenHandle(*handle).location());
fschneider@chromium.orged78ffd2010-07-21 11:05:19 +000077 handle.Dispose();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +000078}
79
80
81void TokenEnumerator::TokenRemoved(Object** token_location) {
82 for (int i = 0; i < token_locations_.length(); ++i) {
83 if (token_locations_[i] == token_location && !token_removed_[i]) {
84 token_removed_[i] = true;
85 return;
86 }
87 }
88}
89
90
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +000091StringsStorage::StringsStorage()
92 : names_(StringsMatch) {
93}
94
95
96StringsStorage::~StringsStorage() {
97 for (HashMap::Entry* p = names_.Start();
98 p != NULL;
99 p = names_.Next(p)) {
100 DeleteArray(reinterpret_cast<const char*>(p->value));
101 }
102}
103
104
105const char* StringsStorage::GetName(String* name) {
106 if (name->IsString()) {
107 char* c_name =
108 name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach();
109 HashMap::Entry* cache_entry = names_.Lookup(c_name, name->Hash(), true);
110 if (cache_entry->value == NULL) {
111 // New entry added.
112 cache_entry->value = c_name;
113 } else {
114 DeleteArray(c_name);
115 }
116 return reinterpret_cast<const char*>(cache_entry->value);
117 }
118 return "";
119}
120
121
lrn@chromium.org25156de2010-04-06 13:10:27 +0000122const char* CodeEntry::kEmptyNamePrefix = "";
ager@chromium.org357bf652010-04-12 11:30:10 +0000123unsigned CodeEntry::next_call_uid_ = 1;
lrn@chromium.org25156de2010-04-06 13:10:27 +0000124
125
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000126void CodeEntry::CopyData(const CodeEntry& source) {
127 call_uid_ = source.call_uid_;
128 tag_ = source.tag_;
129 name_prefix_ = source.name_prefix_;
130 name_ = source.name_;
131 resource_name_ = source.resource_name_;
132 line_number_ = source.line_number_;
133}
134
135
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000136ProfileNode* ProfileNode::FindChild(CodeEntry* entry) {
137 HashMap::Entry* map_entry =
138 children_.Lookup(entry, CodeEntryHash(entry), false);
139 return map_entry != NULL ?
140 reinterpret_cast<ProfileNode*>(map_entry->value) : NULL;
141}
142
143
144ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) {
145 HashMap::Entry* map_entry =
146 children_.Lookup(entry, CodeEntryHash(entry), true);
147 if (map_entry->value == NULL) {
148 // New node added.
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000149 ProfileNode* new_node = new ProfileNode(tree_, entry);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000150 map_entry->value = new_node;
151 children_list_.Add(new_node);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000152 }
153 return reinterpret_cast<ProfileNode*>(map_entry->value);
154}
155
156
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000157double ProfileNode::GetSelfMillis() const {
158 return tree_->TicksToMillis(self_ticks_);
159}
160
161
162double ProfileNode::GetTotalMillis() const {
163 return tree_->TicksToMillis(total_ticks_);
164}
165
166
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000167void ProfileNode::Print(int indent) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000168 OS::Print("%5u %5u %*c %s%s [%d]",
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000169 total_ticks_, self_ticks_,
170 indent, ' ',
ager@chromium.org357bf652010-04-12 11:30:10 +0000171 entry_->name_prefix(),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000172 entry_->name(),
173 entry_->security_token_id());
ager@chromium.org357bf652010-04-12 11:30:10 +0000174 if (entry_->resource_name()[0] != '\0')
175 OS::Print(" %s:%d", entry_->resource_name(), entry_->line_number());
176 OS::Print("\n");
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000177 for (HashMap::Entry* p = children_.Start();
178 p != NULL;
179 p = children_.Next(p)) {
180 reinterpret_cast<ProfileNode*>(p->value)->Print(indent + 2);
181 }
182}
183
184
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000185class DeleteNodesCallback {
186 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000187 void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
188
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000189 void AfterAllChildrenTraversed(ProfileNode* node) {
190 delete node;
191 }
192
193 void AfterChildTraversed(ProfileNode*, ProfileNode*) { }
194};
195
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000196
ager@chromium.org357bf652010-04-12 11:30:10 +0000197ProfileTree::ProfileTree()
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000198 : root_entry_(Logger::FUNCTION_TAG,
199 "",
200 "(root)",
201 "",
202 0,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000203 TokenEnumerator::kNoSecurityToken),
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000204 root_(new ProfileNode(this, &root_entry_)) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000205}
206
207
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000208ProfileTree::~ProfileTree() {
209 DeleteNodesCallback cb;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000210 TraverseDepthFirst(&cb);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000211}
212
213
214void ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path) {
215 ProfileNode* node = root_;
216 for (CodeEntry** entry = path.start() + path.length() - 1;
217 entry != path.start() - 1;
218 --entry) {
219 if (*entry != NULL) {
220 node = node->FindOrAddChild(*entry);
221 }
222 }
223 node->IncrementSelfTicks();
224}
225
226
227void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) {
228 ProfileNode* node = root_;
229 for (CodeEntry** entry = path.start();
230 entry != path.start() + path.length();
231 ++entry) {
232 if (*entry != NULL) {
233 node = node->FindOrAddChild(*entry);
234 }
235 }
236 node->IncrementSelfTicks();
237}
238
239
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000240struct NodesPair {
241 NodesPair(ProfileNode* src, ProfileNode* dst)
242 : src(src), dst(dst) { }
243 ProfileNode* src;
244 ProfileNode* dst;
245};
246
247
248class FilteredCloneCallback {
249 public:
250 explicit FilteredCloneCallback(ProfileNode* dst_root, int security_token_id)
251 : stack_(10),
252 security_token_id_(security_token_id) {
253 stack_.Add(NodesPair(NULL, dst_root));
254 }
255
256 void BeforeTraversingChild(ProfileNode* parent, ProfileNode* child) {
257 if (IsTokenAcceptable(child->entry()->security_token_id(),
258 parent->entry()->security_token_id())) {
259 ProfileNode* clone = stack_.last().dst->FindOrAddChild(child->entry());
260 clone->IncreaseSelfTicks(child->self_ticks());
261 stack_.Add(NodesPair(child, clone));
262 } else {
263 // Attribute ticks to parent node.
264 stack_.last().dst->IncreaseSelfTicks(child->self_ticks());
265 }
266 }
267
268 void AfterAllChildrenTraversed(ProfileNode* parent) { }
269
270 void AfterChildTraversed(ProfileNode*, ProfileNode* child) {
271 if (stack_.last().src == child) {
272 stack_.RemoveLast();
273 }
274 }
275
276 private:
277 bool IsTokenAcceptable(int token, int parent_token) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000278 if (token == TokenEnumerator::kNoSecurityToken
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000279 || token == security_token_id_) return true;
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000280 if (token == TokenEnumerator::kInheritsSecurityToken) {
281 ASSERT(parent_token != TokenEnumerator::kInheritsSecurityToken);
282 return parent_token == TokenEnumerator::kNoSecurityToken
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000283 || parent_token == security_token_id_;
284 }
285 return false;
286 }
287
288 List<NodesPair> stack_;
289 int security_token_id_;
290};
291
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000292void ProfileTree::FilteredClone(ProfileTree* src, int security_token_id) {
293 ms_to_ticks_scale_ = src->ms_to_ticks_scale_;
294 FilteredCloneCallback cb(root_, security_token_id);
295 src->TraverseDepthFirst(&cb);
296 CalculateTotalTicks();
297}
298
299
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000300void ProfileTree::SetTickRatePerMs(double ticks_per_ms) {
301 ms_to_ticks_scale_ = ticks_per_ms > 0 ? 1.0 / ticks_per_ms : 1.0;
302}
303
304
lrn@chromium.org25156de2010-04-06 13:10:27 +0000305class Position {
306 public:
307 explicit Position(ProfileNode* node)
308 : node(node), child_idx_(0) { }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000309 INLINE(ProfileNode* current_child()) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000310 return node->children()->at(child_idx_);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000311 }
lrn@chromium.org25156de2010-04-06 13:10:27 +0000312 INLINE(bool has_current_child()) {
313 return child_idx_ < node->children()->length();
314 }
315 INLINE(void next_child()) { ++child_idx_; }
316
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000317 ProfileNode* node;
lrn@chromium.org25156de2010-04-06 13:10:27 +0000318 private:
319 int child_idx_;
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000320};
321
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000322
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000323// Non-recursive implementation of a depth-first post-order tree traversal.
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000324template <typename Callback>
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000325void ProfileTree::TraverseDepthFirst(Callback* callback) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000326 List<Position> stack(10);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000327 stack.Add(Position(root_));
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000328 while (stack.length() > 0) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000329 Position& current = stack.last();
lrn@chromium.org25156de2010-04-06 13:10:27 +0000330 if (current.has_current_child()) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000331 callback->BeforeTraversingChild(current.node, current.current_child());
lrn@chromium.org25156de2010-04-06 13:10:27 +0000332 stack.Add(Position(current.current_child()));
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000333 } else {
334 callback->AfterAllChildrenTraversed(current.node);
335 if (stack.length() > 1) {
336 Position& parent = stack[stack.length() - 2];
337 callback->AfterChildTraversed(parent.node, current.node);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000338 parent.next_child();
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000339 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000340 // Remove child from the stack.
341 stack.RemoveLast();
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000342 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000343 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000344}
345
346
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000347class CalculateTotalTicksCallback {
348 public:
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000349 void BeforeTraversingChild(ProfileNode*, ProfileNode*) { }
350
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000351 void AfterAllChildrenTraversed(ProfileNode* node) {
352 node->IncreaseTotalTicks(node->self_ticks());
353 }
354
355 void AfterChildTraversed(ProfileNode* parent, ProfileNode* child) {
356 parent->IncreaseTotalTicks(child->total_ticks());
357 }
358};
359
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000360
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000361void ProfileTree::CalculateTotalTicks() {
362 CalculateTotalTicksCallback cb;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000363 TraverseDepthFirst(&cb);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000364}
365
366
367void ProfileTree::ShortPrint() {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000368 OS::Print("root: %u %u %.2fms %.2fms\n",
369 root_->total_ticks(), root_->self_ticks(),
370 root_->GetTotalMillis(), root_->GetSelfMillis());
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000371}
372
373
374void CpuProfile::AddPath(const Vector<CodeEntry*>& path) {
375 top_down_.AddPathFromEnd(path);
376 bottom_up_.AddPathFromStart(path);
377}
378
379
380void CpuProfile::CalculateTotalTicks() {
381 top_down_.CalculateTotalTicks();
382 bottom_up_.CalculateTotalTicks();
383}
384
385
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000386void CpuProfile::SetActualSamplingRate(double actual_sampling_rate) {
387 top_down_.SetTickRatePerMs(actual_sampling_rate);
388 bottom_up_.SetTickRatePerMs(actual_sampling_rate);
389}
390
391
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000392CpuProfile* CpuProfile::FilteredClone(int security_token_id) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000393 ASSERT(security_token_id != TokenEnumerator::kNoSecurityToken);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000394 CpuProfile* clone = new CpuProfile(title_, uid_);
395 clone->top_down_.FilteredClone(&top_down_, security_token_id);
396 clone->bottom_up_.FilteredClone(&bottom_up_, security_token_id);
397 return clone;
398}
399
400
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000401void CpuProfile::ShortPrint() {
402 OS::Print("top down ");
403 top_down_.ShortPrint();
404 OS::Print("bottom up ");
405 bottom_up_.ShortPrint();
406}
407
408
409void CpuProfile::Print() {
410 OS::Print("[Top down]:\n");
411 top_down_.Print();
412 OS::Print("[Bottom up]:\n");
413 bottom_up_.Print();
414}
415
416
417const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL;
418const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue =
419 CodeMap::CodeEntryInfo(NULL, 0);
420
421
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000422void CodeMap::AddAlias(Address start, CodeEntry* entry, Address code_start) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000423 CodeTree::Locator locator;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000424 if (tree_.Find(code_start, &locator)) {
425 const CodeEntryInfo& code_info = locator.value();
426 entry->CopyData(*code_info.entry);
427 tree_.Insert(start, &locator);
428 locator.set_value(CodeEntryInfo(entry, code_info.size));
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000429 }
430}
431
432
433CodeEntry* CodeMap::FindEntry(Address addr) {
434 CodeTree::Locator locator;
435 if (tree_.FindGreatestLessThan(addr, &locator)) {
436 // locator.key() <= addr. Need to check that addr is within entry.
437 const CodeEntryInfo& entry = locator.value();
438 if (addr < (locator.key() + entry.size))
439 return entry.entry;
440 }
441 return NULL;
442}
443
444
lrn@chromium.org25156de2010-04-06 13:10:27 +0000445void CodeMap::CodeTreePrinter::Call(
446 const Address& key, const CodeMap::CodeEntryInfo& value) {
447 OS::Print("%p %5d %s\n", key, value.size, value.entry->name());
448}
449
450
451void CodeMap::Print() {
452 CodeTreePrinter printer;
453 tree_.ForEach(&printer);
454}
455
456
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000457CpuProfilesCollection::CpuProfilesCollection()
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000458 : profiles_uids_(UidsMatch),
lrn@chromium.org25156de2010-04-06 13:10:27 +0000459 current_profiles_semaphore_(OS::CreateSemaphore(1)) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000460 // Create list of unabridged profiles.
461 profiles_by_token_.Add(new List<CpuProfile*>());
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000462}
463
464
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000465static void DeleteArgsCountName(char** name_ptr) {
466 DeleteArray(*name_ptr);
467}
468
469
470static void DeleteCodeEntry(CodeEntry** entry_ptr) {
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000471 delete *entry_ptr;
472}
473
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000474static void DeleteCpuProfile(CpuProfile** profile_ptr) {
475 delete *profile_ptr;
476}
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000477
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000478static void DeleteProfilesList(List<CpuProfile*>** list_ptr) {
479 (*list_ptr)->Iterate(DeleteCpuProfile);
480 delete *list_ptr;
481}
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000482
483CpuProfilesCollection::~CpuProfilesCollection() {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000484 delete current_profiles_semaphore_;
485 current_profiles_.Iterate(DeleteCpuProfile);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000486 profiles_by_token_.Iterate(DeleteProfilesList);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000487 code_entries_.Iterate(DeleteCodeEntry);
488 args_count_names_.Iterate(DeleteArgsCountName);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000489}
490
491
lrn@chromium.org25156de2010-04-06 13:10:27 +0000492bool CpuProfilesCollection::StartProfiling(const char* title, unsigned uid) {
493 ASSERT(uid > 0);
494 current_profiles_semaphore_->Wait();
495 for (int i = 0; i < current_profiles_.length(); ++i) {
496 if (strcmp(current_profiles_[i]->title(), title) == 0) {
497 // Ignore attempts to start profile with the same title.
498 current_profiles_semaphore_->Signal();
499 return false;
500 }
501 }
502 current_profiles_.Add(new CpuProfile(title, uid));
503 current_profiles_semaphore_->Signal();
504 return true;
505}
506
507
508bool CpuProfilesCollection::StartProfiling(String* title, unsigned uid) {
509 return StartProfiling(GetName(title), uid);
510}
511
512
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000513CpuProfile* CpuProfilesCollection::StopProfiling(int security_token_id,
514 const char* title,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000515 double actual_sampling_rate) {
whesse@chromium.orgb6e43bb2010-04-14 09:36:28 +0000516 const int title_len = StrLength(title);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000517 CpuProfile* profile = NULL;
518 current_profiles_semaphore_->Wait();
519 for (int i = current_profiles_.length() - 1; i >= 0; --i) {
520 if (title_len == 0 || strcmp(current_profiles_[i]->title(), title) == 0) {
521 profile = current_profiles_.Remove(i);
522 break;
523 }
524 }
525 current_profiles_semaphore_->Signal();
526
527 if (profile != NULL) {
528 profile->CalculateTotalTicks();
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000529 profile->SetActualSamplingRate(actual_sampling_rate);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000530 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000531 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000532 unabridged_list->Add(profile);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000533 HashMap::Entry* entry =
534 profiles_uids_.Lookup(reinterpret_cast<void*>(profile->uid()),
535 static_cast<uint32_t>(profile->uid()),
536 true);
537 ASSERT(entry->value == NULL);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000538 entry->value = reinterpret_cast<void*>(unabridged_list->length() - 1);
539 return GetProfile(security_token_id, profile->uid());
lrn@chromium.org25156de2010-04-06 13:10:27 +0000540 }
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000541 return NULL;
lrn@chromium.org25156de2010-04-06 13:10:27 +0000542}
543
544
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000545CpuProfile* CpuProfilesCollection::StopProfiling(int security_token_id,
546 String* title,
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000547 double actual_sampling_rate) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000548 return StopProfiling(security_token_id, GetName(title), actual_sampling_rate);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000549}
550
551
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000552CpuProfile* CpuProfilesCollection::GetProfile(int security_token_id,
553 unsigned uid) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000554 HashMap::Entry* entry = profiles_uids_.Lookup(reinterpret_cast<void*>(uid),
555 static_cast<uint32_t>(uid),
556 false);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000557 int index;
558 if (entry != NULL) {
559 index = static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
560 } else {
561 return NULL;
562 }
563 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000564 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
565 if (security_token_id == TokenEnumerator::kNoSecurityToken) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000566 return unabridged_list->at(index);
567 }
568 List<CpuProfile*>* list = GetProfilesList(security_token_id);
569 if (list->at(index) == NULL) {
570 list->at(index) =
571 unabridged_list->at(index)->FilteredClone(security_token_id);
572 }
573 return list->at(index);
574}
575
576
577int CpuProfilesCollection::TokenToIndex(int security_token_id) {
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000578 ASSERT(TokenEnumerator::kNoSecurityToken == -1);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000579 return security_token_id + 1; // kNoSecurityToken -> 0, 0 -> 1, ...
580}
581
582
583List<CpuProfile*>* CpuProfilesCollection::GetProfilesList(
584 int security_token_id) {
585 const int index = TokenToIndex(security_token_id);
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000586 const int lists_to_add = index - profiles_by_token_.length() + 1;
587 if (lists_to_add > 0) profiles_by_token_.AddBlock(NULL, lists_to_add);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000588 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000589 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000590 const int current_count = unabridged_list->length();
591 if (profiles_by_token_[index] == NULL) {
592 profiles_by_token_[index] = new List<CpuProfile*>(current_count);
593 }
594 List<CpuProfile*>* list = profiles_by_token_[index];
ricow@chromium.org30ce4112010-05-31 10:38:25 +0000595 const int profiles_to_add = current_count - list->length();
596 if (profiles_to_add > 0) list->AddBlock(NULL, profiles_to_add);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000597 return list;
598}
599
600
601List<CpuProfile*>* CpuProfilesCollection::Profiles(int security_token_id) {
602 List<CpuProfile*>* unabridged_list =
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000603 profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
604 if (security_token_id == TokenEnumerator::kNoSecurityToken) {
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000605 return unabridged_list;
606 }
607 List<CpuProfile*>* list = GetProfilesList(security_token_id);
608 const int current_count = unabridged_list->length();
609 for (int i = 0; i < current_count; ++i) {
610 if (list->at(i) == NULL) {
611 list->at(i) = unabridged_list->at(i)->FilteredClone(security_token_id);
612 }
613 }
614 return list;
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000615}
616
617
618CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
619 String* name,
620 String* resource_name,
621 int line_number) {
622 CodeEntry* entry = new CodeEntry(tag,
lrn@chromium.org25156de2010-04-06 13:10:27 +0000623 CodeEntry::kEmptyNamePrefix,
ager@chromium.org357bf652010-04-12 11:30:10 +0000624 GetFunctionName(name),
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000625 GetName(resource_name),
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000626 line_number,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000627 TokenEnumerator::kNoSecurityToken);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000628 code_entries_.Add(entry);
629 return entry;
630}
631
632
633CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
634 const char* name) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000635 CodeEntry* entry = new CodeEntry(tag,
636 CodeEntry::kEmptyNamePrefix,
ager@chromium.org357bf652010-04-12 11:30:10 +0000637 GetFunctionName(name),
lrn@chromium.org25156de2010-04-06 13:10:27 +0000638 "",
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000639 v8::CpuProfileNode::kNoLineNumberInfo,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000640 TokenEnumerator::kNoSecurityToken);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000641 code_entries_.Add(entry);
642 return entry;
643}
644
645
646CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
647 const char* name_prefix,
648 String* name) {
649 CodeEntry* entry = new CodeEntry(tag,
650 name_prefix,
651 GetName(name),
652 "",
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000653 v8::CpuProfileNode::kNoLineNumberInfo,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000654 TokenEnumerator::kInheritsSecurityToken);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000655 code_entries_.Add(entry);
656 return entry;
657}
658
659
660CodeEntry* CpuProfilesCollection::NewCodeEntry(Logger::LogEventsAndTags tag,
661 int args_count) {
lrn@chromium.org25156de2010-04-06 13:10:27 +0000662 CodeEntry* entry = new CodeEntry(tag,
663 "args_count: ",
664 GetName(args_count),
665 "",
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000666 v8::CpuProfileNode::kNoLineNumberInfo,
vegorov@chromium.org2356e6f2010-06-09 09:38:56 +0000667 TokenEnumerator::kInheritsSecurityToken);
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000668 code_entries_.Add(entry);
669 return entry;
670}
671
672
673CodeEntry* CpuProfilesCollection::NewCodeEntry(int security_token_id) {
674 CodeEntry* entry = new CodeEntry(security_token_id);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000675 code_entries_.Add(entry);
676 return entry;
677}
678
679
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000680const char* CpuProfilesCollection::GetName(int args_count) {
681 ASSERT(args_count >= 0);
682 if (args_count_names_.length() <= args_count) {
683 args_count_names_.AddBlock(
684 NULL, args_count - args_count_names_.length() + 1);
685 }
686 if (args_count_names_[args_count] == NULL) {
687 const int kMaximumNameLength = 32;
688 char* name = NewArray<char>(kMaximumNameLength);
lrn@chromium.org25156de2010-04-06 13:10:27 +0000689 OS::SNPrintF(Vector<char>(name, kMaximumNameLength), "%d", args_count);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000690 args_count_names_[args_count] = name;
691 }
692 return args_count_names_[args_count];
693}
694
695
lrn@chromium.org25156de2010-04-06 13:10:27 +0000696void CpuProfilesCollection::AddPathToCurrentProfiles(
697 const Vector<CodeEntry*>& path) {
698 // As starting / stopping profiles is rare relatively to this
699 // method, we don't bother minimizing the duration of lock holding,
700 // e.g. copying contents of the list to a local vector.
701 current_profiles_semaphore_->Wait();
702 for (int i = 0; i < current_profiles_.length(); ++i) {
703 current_profiles_[i]->AddPath(path);
704 }
705 current_profiles_semaphore_->Signal();
706}
707
708
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000709void SampleRateCalculator::Tick() {
710 if (--wall_time_query_countdown_ == 0)
711 UpdateMeasurements(OS::TimeCurrentMillis());
712}
713
714
715void SampleRateCalculator::UpdateMeasurements(double current_time) {
716 if (measurements_count_++ != 0) {
717 const double measured_ticks_per_ms =
718 (kWallTimeQueryIntervalMs * ticks_per_ms_) /
719 (current_time - last_wall_time_);
720 // Update the average value.
721 ticks_per_ms_ +=
722 (measured_ticks_per_ms - ticks_per_ms_) / measurements_count_;
723 // Update the externally accessible result.
724 result_ = static_cast<AtomicWord>(ticks_per_ms_ * kResultScale);
725 }
726 last_wall_time_ = current_time;
727 wall_time_query_countdown_ =
728 static_cast<unsigned>(kWallTimeQueryIntervalMs * ticks_per_ms_);
729}
730
731
ager@chromium.org357bf652010-04-12 11:30:10 +0000732const char* ProfileGenerator::kAnonymousFunctionName = "(anonymous function)";
733const char* ProfileGenerator::kProgramEntryName = "(program)";
734const char* ProfileGenerator::kGarbageCollectorEntryName =
735 "(garbage collector)";
736
737
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000738ProfileGenerator::ProfileGenerator(CpuProfilesCollection* profiles)
ager@chromium.org357bf652010-04-12 11:30:10 +0000739 : profiles_(profiles),
740 program_entry_(
741 profiles->NewCodeEntry(Logger::FUNCTION_TAG, kProgramEntryName)),
742 gc_entry_(
743 profiles->NewCodeEntry(Logger::BUILTIN_TAG,
744 kGarbageCollectorEntryName)) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000745}
746
747
748void ProfileGenerator::RecordTickSample(const TickSample& sample) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000749 // Allocate space for stack frames + pc + function + vm-state.
750 ScopedVector<CodeEntry*> entries(sample.frames_count + 3);
751 // As actual number of decoded code entries may vary, initialize
752 // entries vector with NULL values.
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000753 CodeEntry** entry = entries.start();
ager@chromium.org357bf652010-04-12 11:30:10 +0000754 memset(entry, 0, entries.length() * sizeof(*entry));
755 if (sample.pc != NULL) {
756 *entry++ = code_map_.FindEntry(sample.pc);
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000757
ager@chromium.org357bf652010-04-12 11:30:10 +0000758 if (sample.function != NULL) {
759 *entry = code_map_.FindEntry(sample.function);
760 if (*entry != NULL && !(*entry)->is_js_function()) {
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000761 *entry = NULL;
ager@chromium.org357bf652010-04-12 11:30:10 +0000762 } else {
763 CodeEntry* pc_entry = *entries.start();
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000764 if (pc_entry == NULL) {
ager@chromium.org357bf652010-04-12 11:30:10 +0000765 *entry = NULL;
erik.corry@gmail.com9dfbea42010-05-21 12:58:28 +0000766 } else if (pc_entry->is_js_function()) {
767 // Use function entry in favor of pc entry, as function
768 // entry has security token.
769 *entries.start() = NULL;
770 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000771 }
772 entry++;
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000773 }
ager@chromium.org357bf652010-04-12 11:30:10 +0000774
775 for (const Address *stack_pos = sample.stack,
776 *stack_end = stack_pos + sample.frames_count;
777 stack_pos != stack_end;
778 ++stack_pos) {
779 *entry++ = code_map_.FindEntry(*stack_pos);
780 }
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000781 }
782
ager@chromium.org357bf652010-04-12 11:30:10 +0000783 if (FLAG_prof_browser_mode) {
ricow@chromium.orgc9c80822010-04-21 08:22:37 +0000784 bool no_symbolized_entries = true;
785 for (CodeEntry** e = entries.start(); e != entry; ++e) {
786 if (*e != NULL) {
787 no_symbolized_entries = false;
788 break;
789 }
790 }
791 // If no frames were symbolized, put the VM state entry in.
792 if (no_symbolized_entries) {
793 *entry++ = EntryForVMState(sample.state);
794 }
whesse@chromium.orgcec079d2010-03-22 14:44:04 +0000795 }
796
lrn@chromium.org25156de2010-04-06 13:10:27 +0000797 profiles_->AddPathToCurrentProfiles(entries);
fschneider@chromium.org086aac62010-03-17 13:18:24 +0000798}
799
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000800
801HeapGraphEdge::HeapGraphEdge(Type type,
802 const char* name,
803 HeapEntry* from,
804 HeapEntry* to)
805 : type_(type), name_(name), from_(from), to_(to) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000806 ASSERT(type_ == CONTEXT_VARIABLE || type_ == PROPERTY || type_ == INTERNAL);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000807}
808
809
810HeapGraphEdge::HeapGraphEdge(int index,
811 HeapEntry* from,
812 HeapEntry* to)
813 : type_(ELEMENT), index_(index), from_(from), to_(to) {
814}
815
816
817static void DeleteHeapGraphEdge(HeapGraphEdge** edge_ptr) {
818 delete *edge_ptr;
819}
820
821
822static void DeleteHeapGraphPath(HeapGraphPath** path_ptr) {
823 delete *path_ptr;
824}
825
826
827HeapEntry::~HeapEntry() {
828 children_.Iterate(DeleteHeapGraphEdge);
829 retaining_paths_.Iterate(DeleteHeapGraphPath);
830}
831
832
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000833void HeapEntry::AddEdge(HeapGraphEdge* edge) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000834 children_.Add(edge);
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000835 edge->to()->retainers_.Add(edge);
836}
837
838
839void HeapEntry::SetClosureReference(const char* name, HeapEntry* entry) {
840 AddEdge(
841 new HeapGraphEdge(HeapGraphEdge::CONTEXT_VARIABLE, name, this, entry));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000842}
843
844
845void HeapEntry::SetElementReference(int index, HeapEntry* entry) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000846 AddEdge(new HeapGraphEdge(index, this, entry));
847}
848
849
850void HeapEntry::SetInternalReference(const char* name, HeapEntry* entry) {
851 AddEdge(new HeapGraphEdge(HeapGraphEdge::INTERNAL, name, this, entry));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000852}
853
854
855void HeapEntry::SetPropertyReference(const char* name, HeapEntry* entry) {
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +0000856 AddEdge(new HeapGraphEdge(HeapGraphEdge::PROPERTY, name, this, entry));
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000857}
858
859
860void HeapEntry::SetAutoIndexReference(HeapEntry* entry) {
861 SetElementReference(next_auto_index_++, entry);
862}
863
864
ricow@chromium.org4980dff2010-07-19 08:33:45 +0000865void HeapEntry::SetUnidirAutoIndexReference(HeapEntry* entry) {
866 children_.Add(new HeapGraphEdge(next_auto_index_++, this, entry));
867}
868
869
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000870int HeapEntry::TotalSize() {
871 return total_size_ != kUnknownSize ? total_size_ : CalculateTotalSize();
872}
873
874
875int HeapEntry::NonSharedTotalSize() {
876 return non_shared_total_size_ != kUnknownSize ?
877 non_shared_total_size_ : CalculateNonSharedTotalSize();
878}
879
880
ricow@chromium.org4980dff2010-07-19 08:33:45 +0000881template<class Visitor>
882void HeapEntry::ApplyAndPaintAllReachable(Visitor* visitor) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000883 List<HeapEntry*> list(10);
884 list.Add(this);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000885 this->PaintReachable();
ricow@chromium.org4980dff2010-07-19 08:33:45 +0000886 visitor->Apply(this);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000887 while (!list.is_empty()) {
888 HeapEntry* entry = list.RemoveLast();
889 const int children_count = entry->children_.length();
890 for (int i = 0; i < children_count; ++i) {
891 HeapEntry* child = entry->children_[i]->to();
892 if (!child->painted_reachable()) {
893 list.Add(child);
894 child->PaintReachable();
ricow@chromium.org4980dff2010-07-19 08:33:45 +0000895 visitor->Apply(child);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000896 }
897 }
898 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000899}
900
901
ricow@chromium.org4980dff2010-07-19 08:33:45 +0000902class NullClass {
903 public:
904 void Apply(HeapEntry* entry) { }
905};
906
907void HeapEntry::PaintAllReachable() {
908 NullClass null;
909 ApplyAndPaintAllReachable(&null);
910}
911
912
913class TotalSizeCalculator {
914 public:
915 TotalSizeCalculator()
916 : total_size_(0) {
917 }
918
919 int total_size() const { return total_size_; }
920
921 void Apply(HeapEntry* entry) {
922 total_size_ += entry->self_size();
923 }
924
925 private:
926 int total_size_;
927};
928
929int HeapEntry::CalculateTotalSize() {
930 snapshot_->ClearPaint();
931 TotalSizeCalculator calc;
932 ApplyAndPaintAllReachable(&calc);
933 total_size_ = calc.total_size();
934 return total_size_;
935}
936
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000937
938class NonSharedSizeCalculator {
939 public:
940 NonSharedSizeCalculator()
941 : non_shared_total_size_(0) {
942 }
943
944 int non_shared_total_size() const { return non_shared_total_size_; }
945
946 void Apply(HeapEntry* entry) {
947 if (entry->painted_reachable()) {
948 non_shared_total_size_ += entry->self_size();
949 }
950 }
951
952 private:
953 int non_shared_total_size_;
954};
955
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000956int HeapEntry::CalculateNonSharedTotalSize() {
957 // To calculate non-shared total size, first we paint all reachable
958 // nodes in one color, then we paint all nodes reachable from other
959 // nodes with a different color. Then we consider only nodes painted
ricow@chromium.org4980dff2010-07-19 08:33:45 +0000960 // with the first color for calculating the total size.
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000961 snapshot_->ClearPaint();
ricow@chromium.org4980dff2010-07-19 08:33:45 +0000962 PaintAllReachable();
963
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000964 List<HeapEntry*> list(10);
ricow@chromium.org4980dff2010-07-19 08:33:45 +0000965 if (this != snapshot_->root()) {
966 list.Add(snapshot_->root());
967 snapshot_->root()->PaintReachableFromOthers();
968 }
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000969 while (!list.is_empty()) {
970 HeapEntry* entry = list.RemoveLast();
971 const int children_count = entry->children_.length();
972 for (int i = 0; i < children_count; ++i) {
973 HeapEntry* child = entry->children_[i]->to();
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000974 if (child != this && child->not_painted_reachable_from_others()) {
ricow@chromium.org4980dff2010-07-19 08:33:45 +0000975 list.Add(child);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000976 child->PaintReachableFromOthers();
977 }
978 }
979 }
980
981 NonSharedSizeCalculator calculator;
982 snapshot_->IterateEntries(&calculator);
ricow@chromium.org4980dff2010-07-19 08:33:45 +0000983 non_shared_total_size_ = calculator.non_shared_total_size();
984 return non_shared_total_size_;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +0000985}
986
987
988class CachedHeapGraphPath {
989 public:
990 CachedHeapGraphPath()
991 : nodes_(NodesMatch) { }
992 CachedHeapGraphPath(const CachedHeapGraphPath& src)
993 : nodes_(NodesMatch, &HashMap::DefaultAllocator, src.nodes_.capacity()),
994 path_(src.path_.length() + 1) {
995 for (HashMap::Entry* p = src.nodes_.Start();
996 p != NULL;
997 p = src.nodes_.Next(p)) {
998 nodes_.Lookup(p->key, p->hash, true);
999 }
1000 path_.AddAll(src.path_);
1001 }
1002 void Add(HeapGraphEdge* edge) {
1003 nodes_.Lookup(edge->to(), Hash(edge->to()), true);
1004 path_.Add(edge);
1005 }
1006 bool ContainsNode(HeapEntry* node) {
1007 return nodes_.Lookup(node, Hash(node), false) != NULL;
1008 }
1009 const List<HeapGraphEdge*>* path() const { return &path_; }
1010
1011 private:
1012 static uint32_t Hash(HeapEntry* entry) {
1013 return static_cast<uint32_t>(reinterpret_cast<intptr_t>(entry));
1014 }
1015 static bool NodesMatch(void* key1, void* key2) { return key1 == key2; }
1016
1017 HashMap nodes_;
1018 List<HeapGraphEdge*> path_;
1019};
1020
1021
1022const List<HeapGraphPath*>* HeapEntry::GetRetainingPaths() {
1023 if (retaining_paths_.length() == 0 && retainers_.length() != 0) {
1024 CachedHeapGraphPath path;
1025 FindRetainingPaths(this, &path);
1026 }
1027 return &retaining_paths_;
1028}
1029
1030
1031void HeapEntry::FindRetainingPaths(HeapEntry* node,
1032 CachedHeapGraphPath* prev_path) {
1033 for (int i = 0; i < node->retainers_.length(); ++i) {
1034 HeapGraphEdge* ret_edge = node->retainers_[i];
1035 if (prev_path->ContainsNode(ret_edge->from())) continue;
1036 if (ret_edge->from() != snapshot_->root()) {
1037 CachedHeapGraphPath path(*prev_path);
1038 path.Add(ret_edge);
1039 FindRetainingPaths(ret_edge->from(), &path);
1040 } else {
1041 HeapGraphPath* ret_path = new HeapGraphPath(*prev_path->path());
1042 ret_path->Set(0, ret_edge);
1043 retaining_paths_.Add(ret_path);
1044 }
1045 }
1046}
1047
1048
1049static void RemoveEdge(List<HeapGraphEdge*>* list, HeapGraphEdge* edge) {
1050 for (int i = 0; i < list->length(); ) {
1051 if (list->at(i) == edge) {
1052 list->Remove(i);
1053 return;
1054 } else {
1055 ++i;
1056 }
1057 }
1058 UNREACHABLE();
1059}
1060
1061
1062void HeapEntry::RemoveChild(HeapGraphEdge* edge) {
1063 RemoveEdge(&children_, edge);
1064 delete edge;
1065}
1066
1067
1068void HeapEntry::RemoveRetainer(HeapGraphEdge* edge) {
1069 RemoveEdge(&retainers_, edge);
1070}
1071
1072
1073void HeapEntry::CutEdges() {
1074 for (int i = 0; i < children_.length(); ++i) {
1075 HeapGraphEdge* edge = children_[i];
1076 edge->to()->RemoveRetainer(edge);
1077 }
1078 children_.Iterate(DeleteHeapGraphEdge);
1079 children_.Clear();
1080
1081 for (int i = 0; i < retainers_.length(); ++i) {
1082 HeapGraphEdge* edge = retainers_[i];
1083 edge->from()->RemoveChild(edge);
1084 }
1085 retainers_.Clear();
1086}
1087
1088
1089void HeapEntry::Print(int max_depth, int indent) {
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001090 OS::Print("%6d %6d %6d [%ld] ",
1091 self_size_, TotalSize(), NonSharedTotalSize(), id_);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001092 if (type_ != STRING) {
1093 OS::Print("%s %.40s\n", TypeAsString(), name_);
1094 } else {
1095 OS::Print("\"");
1096 const char* c = name_;
1097 while (*c && (c - name_) <= 40) {
1098 if (*c != '\n')
1099 OS::Print("%c", *c);
1100 else
1101 OS::Print("\\n");
1102 ++c;
1103 }
1104 OS::Print("\"\n");
1105 }
1106 if (--max_depth == 0) return;
1107 const int children_count = children_.length();
1108 for (int i = 0; i < children_count; ++i) {
1109 HeapGraphEdge* edge = children_[i];
1110 switch (edge->type()) {
1111 case HeapGraphEdge::CONTEXT_VARIABLE:
1112 OS::Print(" %*c #%s: ", indent, ' ', edge->name());
1113 break;
1114 case HeapGraphEdge::ELEMENT:
1115 OS::Print(" %*c %d: ", indent, ' ', edge->index());
1116 break;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00001117 case HeapGraphEdge::INTERNAL:
1118 OS::Print(" %*c $%s: ", indent, ' ', edge->name());
1119 break;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001120 case HeapGraphEdge::PROPERTY:
1121 OS::Print(" %*c %s: ", indent, ' ', edge->name());
1122 break;
1123 default:
1124 OS::Print("!!! unknown edge type: %d ", edge->type());
1125 }
1126 edge->to()->Print(max_depth, indent + 2);
1127 }
1128}
1129
1130
1131const char* HeapEntry::TypeAsString() {
1132 switch (type_) {
1133 case INTERNAL: return "/internal/";
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001134 case OBJECT: return "/object/";
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001135 case CLOSURE: return "/closure/";
1136 case STRING: return "/string/";
1137 case CODE: return "/code/";
1138 case ARRAY: return "/array/";
1139 default: return "???";
1140 }
1141}
1142
1143
1144HeapGraphPath::HeapGraphPath(const List<HeapGraphEdge*>& path)
1145 : path_(path.length() + 1) {
1146 Add(NULL);
1147 for (int i = path.length() - 1; i >= 0; --i) {
1148 Add(path[i]);
1149 }
1150}
1151
1152
1153void HeapGraphPath::Print() {
1154 path_[0]->from()->Print(1, 0);
1155 for (int i = 0; i < path_.length(); ++i) {
1156 OS::Print(" -> ");
1157 HeapGraphEdge* edge = path_[i];
1158 switch (edge->type()) {
1159 case HeapGraphEdge::CONTEXT_VARIABLE:
1160 OS::Print("[#%s] ", edge->name());
1161 break;
1162 case HeapGraphEdge::ELEMENT:
1163 OS::Print("[%d] ", edge->index());
1164 break;
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00001165 case HeapGraphEdge::INTERNAL:
1166 OS::Print("[$%s] ", edge->name());
1167 break;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001168 case HeapGraphEdge::PROPERTY:
1169 OS::Print("[%s] ", edge->name());
1170 break;
1171 default:
1172 OS::Print("!!! unknown edge type: %d ", edge->type());
1173 }
1174 edge->to()->Print(1, 0);
1175 }
1176 OS::Print("\n");
1177}
1178
1179
1180class IndexedReferencesExtractor : public ObjectVisitor {
1181 public:
1182 IndexedReferencesExtractor(HeapSnapshot* snapshot, HeapEntry* parent)
1183 : snapshot_(snapshot),
1184 parent_(parent) {
1185 }
1186
1187 void VisitPointer(Object** o) {
1188 if (!(*o)->IsHeapObject()) return;
1189 HeapEntry* entry = snapshot_->GetEntry(HeapObject::cast(*o));
1190 if (entry != NULL) {
1191 parent_->SetAutoIndexReference(entry);
1192 }
1193 }
1194
1195 void VisitPointers(Object** start, Object** end) {
1196 for (Object** p = start; p < end; p++) VisitPointer(p);
1197 }
1198
1199 private:
1200 HeapSnapshot* snapshot_;
1201 HeapEntry* parent_;
1202};
1203
1204
1205HeapEntriesMap::HeapEntriesMap()
1206 : entries_(HeapObjectsMatch) {
1207}
1208
1209
1210HeapEntriesMap::~HeapEntriesMap() {
1211 for (HashMap::Entry* p = entries_.Start();
1212 p != NULL;
1213 p = entries_.Next(p)) {
1214 if (!IsAlias(p->value)) delete reinterpret_cast<HeapEntry*>(p->value);
1215 }
1216}
1217
1218
1219void HeapEntriesMap::Alias(HeapObject* object, HeapEntry* entry) {
1220 HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true);
1221 if (cache_entry->value == NULL)
1222 cache_entry->value = reinterpret_cast<void*>(
1223 reinterpret_cast<intptr_t>(entry) | kAliasTag);
1224}
1225
1226
1227void HeapEntriesMap::Apply(void (HeapEntry::*Func)(void)) {
1228 for (HashMap::Entry* p = entries_.Start();
1229 p != NULL;
1230 p = entries_.Next(p)) {
1231 if (!IsAlias(p->value)) (reinterpret_cast<HeapEntry*>(p->value)->*Func)();
1232 }
1233}
1234
1235
1236HeapEntry* HeapEntriesMap::Map(HeapObject* object) {
1237 HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), false);
1238 return cache_entry != NULL ?
1239 reinterpret_cast<HeapEntry*>(
1240 reinterpret_cast<intptr_t>(cache_entry->value) & (~kAliasTag)) : NULL;
1241}
1242
1243
1244void HeapEntriesMap::Pair(HeapObject* object, HeapEntry* entry) {
1245 HashMap::Entry* cache_entry = entries_.Lookup(object, Hash(object), true);
1246 ASSERT(cache_entry->value == NULL);
1247 cache_entry->value = entry;
1248}
1249
1250
1251HeapSnapshot::HeapSnapshot(HeapSnapshotsCollection* collection,
1252 const char* title,
1253 unsigned uid)
1254 : collection_(collection),
1255 title_(title),
1256 uid_(uid),
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001257 root_(this),
1258 sorted_entries_(NULL) {
1259}
1260
1261
1262HeapSnapshot::~HeapSnapshot() {
1263 delete sorted_entries_;
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001264}
1265
1266
1267void HeapSnapshot::ClearPaint() {
1268 root_.ClearPaint();
1269 entries_.Apply(&HeapEntry::ClearPaint);
1270}
1271
1272
1273HeapEntry* HeapSnapshot::GetEntry(Object* obj) {
1274 if (!obj->IsHeapObject()) return NULL;
1275 HeapObject* object = HeapObject::cast(obj);
1276
1277 {
1278 HeapEntry* existing = FindEntry(object);
1279 if (existing != NULL) return existing;
1280 }
1281
1282 // Add new entry.
1283 if (object->IsJSFunction()) {
1284 JSFunction* func = JSFunction::cast(object);
1285 SharedFunctionInfo* shared = func->shared();
1286 String* name = String::cast(shared->name())->length() > 0 ?
1287 String::cast(shared->name()) : shared->inferred_name();
1288 return AddEntry(object, HeapEntry::CLOSURE, collection_->GetName(name));
1289 } else if (object->IsJSObject()) {
1290 return AddEntry(object,
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001291 HeapEntry::OBJECT,
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001292 collection_->GetName(
1293 JSObject::cast(object)->constructor_name()));
1294 } else if (object->IsJSGlobalPropertyCell()) {
1295 HeapEntry* value = GetEntry(JSGlobalPropertyCell::cast(object)->value());
1296 // If GPC references an object that we have interest in, add the object.
1297 // We don't store HeapEntries for GPCs. Instead, we make our hash map
1298 // to point to object's HeapEntry by GPCs address.
1299 if (value != NULL) AddEntryAlias(object, value);
1300 return value;
1301 } else if (object->IsString()) {
1302 return AddEntry(object,
1303 HeapEntry::STRING,
1304 collection_->GetName(String::cast(object)));
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001305 } else if (object->IsCode()) {
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001306 return AddEntry(object, HeapEntry::CODE);
whesse@chromium.org2c186ca2010-06-16 11:32:39 +00001307 } else if (object->IsSharedFunctionInfo()) {
1308 SharedFunctionInfo* shared = SharedFunctionInfo::cast(object);
1309 String* name = String::cast(shared->name())->length() > 0 ?
1310 String::cast(shared->name()) : shared->inferred_name();
1311 return AddEntry(object, HeapEntry::CODE, collection_->GetName(name));
1312 } else if (object->IsScript()) {
1313 Script* script = Script::cast(object);
1314 return AddEntry(object,
1315 HeapEntry::CODE,
1316 script->name()->IsString() ?
1317 collection_->GetName(String::cast(script->name())) : "");
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001318 } else if (object->IsFixedArray()) {
1319 return AddEntry(object, HeapEntry::ARRAY);
1320 }
1321 // No interest in this object.
1322 return NULL;
1323}
1324
1325
1326void HeapSnapshot::SetClosureReference(HeapEntry* parent,
1327 String* reference_name,
1328 Object* child) {
1329 HeapEntry* child_entry = GetEntry(child);
1330 if (child_entry != NULL) {
1331 parent->SetClosureReference(
1332 collection_->GetName(reference_name), child_entry);
1333 }
1334}
1335
1336
1337void HeapSnapshot::SetElementReference(HeapEntry* parent,
1338 int index,
1339 Object* child) {
1340 HeapEntry* child_entry = GetEntry(child);
1341 if (child_entry != NULL) {
1342 parent->SetElementReference(index, child_entry);
1343 }
1344}
1345
1346
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00001347void HeapSnapshot::SetInternalReference(HeapEntry* parent,
1348 const char* reference_name,
1349 Object* child) {
1350 HeapEntry* child_entry = GetEntry(child);
1351 if (child_entry != NULL) {
1352 parent->SetInternalReference(reference_name, child_entry);
1353 }
1354}
1355
1356
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001357void HeapSnapshot::SetPropertyReference(HeapEntry* parent,
1358 String* reference_name,
1359 Object* child) {
1360 HeapEntry* child_entry = GetEntry(child);
1361 if (child_entry != NULL) {
1362 parent->SetPropertyReference(
1363 collection_->GetName(reference_name), child_entry);
1364 }
1365}
1366
1367
1368HeapEntry* HeapSnapshot::AddEntry(HeapObject* object,
1369 HeapEntry::Type type,
1370 const char* name) {
1371 HeapEntry* entry = new HeapEntry(this,
1372 type,
1373 name,
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001374 collection_->GetObjectId(object->address()),
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001375 GetObjectSize(object),
1376 GetObjectSecurityToken(object));
1377 entries_.Pair(object, entry);
1378
1379 // Detect, if this is a JS global object of the current context, and
1380 // add it to snapshot's roots. There can be several JS global objects
1381 // in a context.
1382 if (object->IsJSGlobalProxy()) {
1383 int global_security_token = GetGlobalSecurityToken();
1384 int object_security_token =
1385 collection_->token_enumerator()->GetTokenId(
1386 Context::cast(
1387 JSGlobalProxy::cast(object)->context())->security_token());
1388 if (object_security_token == TokenEnumerator::kNoSecurityToken
1389 || object_security_token == global_security_token) {
1390 HeapEntry* global_object_entry =
1391 GetEntry(HeapObject::cast(object->map()->prototype()));
1392 ASSERT(global_object_entry != NULL);
1393 root_.SetAutoIndexReference(global_object_entry);
1394 }
1395 }
1396
1397 return entry;
1398}
1399
1400
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001401class EdgesCutter {
1402 public:
1403 explicit EdgesCutter(int global_security_token)
1404 : global_security_token_(global_security_token) {
1405 }
1406
1407 void Apply(HeapEntry* entry) {
1408 if (entry->security_token_id() != TokenEnumerator::kNoSecurityToken
1409 && entry->security_token_id() != global_security_token_) {
1410 entry->CutEdges();
1411 }
1412 }
1413
1414 private:
1415 const int global_security_token_;
1416};
1417
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001418void HeapSnapshot::CutObjectsFromForeignSecurityContexts() {
1419 EdgesCutter cutter(GetGlobalSecurityToken());
1420 entries_.Apply(&cutter);
1421}
1422
1423
1424int HeapSnapshot::GetGlobalSecurityToken() {
1425 return collection_->token_enumerator()->GetTokenId(
1426 Top::context()->global()->global_context()->security_token());
1427}
1428
1429
1430int HeapSnapshot::GetObjectSize(HeapObject* obj) {
1431 return obj->IsJSObject() ?
1432 CalculateNetworkSize(JSObject::cast(obj)) : obj->Size();
1433}
1434
1435
1436int HeapSnapshot::GetObjectSecurityToken(HeapObject* obj) {
1437 if (obj->IsGlobalContext()) {
1438 return collection_->token_enumerator()->GetTokenId(
1439 Context::cast(obj)->security_token());
1440 } else {
1441 return TokenEnumerator::kNoSecurityToken;
1442 }
1443}
1444
1445
1446int HeapSnapshot::CalculateNetworkSize(JSObject* obj) {
1447 int size = obj->Size();
1448 // If 'properties' and 'elements' are non-empty (thus, non-shared),
1449 // take their size into account.
1450 if (FixedArray::cast(obj->properties())->length() != 0) {
1451 size += obj->properties()->Size();
1452 }
1453 if (FixedArray::cast(obj->elements())->length() != 0) {
1454 size += obj->elements()->Size();
1455 }
1456 // For functions, also account non-empty context and literals sizes.
1457 if (obj->IsJSFunction()) {
1458 JSFunction* f = JSFunction::cast(obj);
1459 if (f->unchecked_context()->IsContext()) {
1460 size += f->context()->Size();
1461 }
1462 if (f->literals()->length() != 0) {
1463 size += f->literals()->Size();
1464 }
1465 }
1466 return size;
1467}
1468
1469
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001470class EntriesCollector {
1471 public:
1472 explicit EntriesCollector(List<HeapEntry*>* list) : list_(list) { }
1473 void Apply(HeapEntry* entry) {
1474 list_->Add(entry);
1475 }
1476 private:
1477 List<HeapEntry*>* list_;
1478};
1479
1480template<class T>
1481static int SortByIds(const T* entry1_ptr,
1482 const T* entry2_ptr) {
1483 if ((*entry1_ptr)->id() == (*entry2_ptr)->id()) return 0;
1484 return (*entry1_ptr)->id() < (*entry2_ptr)->id() ? -1 : 1;
1485}
1486
1487List<HeapEntry*>* HeapSnapshot::GetSortedEntriesList() {
1488 if (sorted_entries_ != NULL) return sorted_entries_;
1489 sorted_entries_ = new List<HeapEntry*>(entries_.capacity());
1490 EntriesCollector collector(sorted_entries_);
1491 entries_.Apply(&collector);
1492 sorted_entries_->Sort(SortByIds);
1493 return sorted_entries_;
1494}
1495
1496
1497HeapSnapshotsDiff* HeapSnapshot::CompareWith(HeapSnapshot* snapshot) {
1498 return collection_->CompareSnapshots(this, snapshot);
1499}
1500
1501
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001502void HeapSnapshot::Print(int max_depth) {
1503 root_.Print(max_depth, 0);
1504}
1505
1506
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001507HeapObjectsMap::HeapObjectsMap()
1508 : initial_fill_mode_(true),
1509 next_id_(1),
1510 entries_map_(AddressesMatch),
1511 entries_(new List<EntryInfo>()) { }
1512
1513
1514HeapObjectsMap::~HeapObjectsMap() {
1515 delete entries_;
1516}
1517
1518
1519void HeapObjectsMap::SnapshotGenerationFinished() {
1520 initial_fill_mode_ = false;
1521 RemoveDeadEntries();
1522}
1523
1524
1525uint64_t HeapObjectsMap::FindObject(Address addr) {
1526 if (!initial_fill_mode_) {
1527 uint64_t existing = FindEntry(addr);
1528 if (existing != 0) return existing;
1529 }
1530 uint64_t id = next_id_++;
1531 AddEntry(addr, id);
1532 return id;
1533}
1534
1535
1536void HeapObjectsMap::MoveObject(Address from, Address to) {
1537 if (from == to) return;
1538 HashMap::Entry* entry = entries_map_.Lookup(from, AddressHash(from), false);
1539 if (entry != NULL) {
1540 void* value = entry->value;
1541 entries_map_.Remove(from, AddressHash(from));
1542 entry = entries_map_.Lookup(to, AddressHash(to), true);
1543 // We can have an entry at the new location, it is OK, as GC can overwrite
1544 // dead objects with alive objects being moved.
1545 entry->value = value;
1546 }
1547}
1548
1549
1550void HeapObjectsMap::AddEntry(Address addr, uint64_t id) {
1551 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), true);
1552 ASSERT(entry->value == NULL);
1553 entry->value = reinterpret_cast<void*>(entries_->length());
1554 entries_->Add(EntryInfo(id));
1555}
1556
1557
1558uint64_t HeapObjectsMap::FindEntry(Address addr) {
1559 HashMap::Entry* entry = entries_map_.Lookup(addr, AddressHash(addr), false);
1560 if (entry != NULL) {
1561 int entry_index =
1562 static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
1563 EntryInfo& entry_info = entries_->at(entry_index);
1564 entry_info.accessed = true;
1565 return entry_info.id;
1566 } else {
1567 return 0;
1568 }
1569}
1570
1571
1572void HeapObjectsMap::RemoveDeadEntries() {
1573 List<EntryInfo>* new_entries = new List<EntryInfo>();
1574 for (HashMap::Entry* entry = entries_map_.Start();
1575 entry != NULL;
1576 entry = entries_map_.Next(entry)) {
1577 int entry_index =
1578 static_cast<int>(reinterpret_cast<intptr_t>(entry->value));
1579 EntryInfo& entry_info = entries_->at(entry_index);
1580 if (entry_info.accessed) {
1581 entry->value = reinterpret_cast<void*>(new_entries->length());
1582 new_entries->Add(EntryInfo(entry_info.id, false));
1583 }
1584 }
1585 delete entries_;
1586 entries_ = new_entries;
1587}
1588
1589
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001590HeapSnapshotsCollection::HeapSnapshotsCollection()
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001591 : is_tracking_objects_(false),
1592 snapshots_uids_(HeapSnapshotsMatch),
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001593 token_enumerator_(new TokenEnumerator()) {
1594}
1595
1596
1597static void DeleteHeapSnapshot(HeapSnapshot** snapshot_ptr) {
1598 delete *snapshot_ptr;
1599}
1600
1601
1602HeapSnapshotsCollection::~HeapSnapshotsCollection() {
1603 delete token_enumerator_;
1604 snapshots_.Iterate(DeleteHeapSnapshot);
1605}
1606
1607
1608HeapSnapshot* HeapSnapshotsCollection::NewSnapshot(const char* name,
1609 unsigned uid) {
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001610 is_tracking_objects_ = true; // Start watching for heap objects moves.
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001611 HeapSnapshot* snapshot = new HeapSnapshot(this, name, uid);
1612 snapshots_.Add(snapshot);
1613 HashMap::Entry* entry =
1614 snapshots_uids_.Lookup(reinterpret_cast<void*>(snapshot->uid()),
1615 static_cast<uint32_t>(snapshot->uid()),
1616 true);
1617 ASSERT(entry->value == NULL);
1618 entry->value = snapshot;
1619 return snapshot;
1620}
1621
1622
1623HeapSnapshot* HeapSnapshotsCollection::GetSnapshot(unsigned uid) {
1624 HashMap::Entry* entry = snapshots_uids_.Lookup(reinterpret_cast<void*>(uid),
1625 static_cast<uint32_t>(uid),
1626 false);
1627 return entry != NULL ? reinterpret_cast<HeapSnapshot*>(entry->value) : NULL;
1628}
1629
1630
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001631HeapSnapshotsDiff* HeapSnapshotsCollection::CompareSnapshots(
1632 HeapSnapshot* snapshot1,
1633 HeapSnapshot* snapshot2) {
1634 return comparator_.Compare(snapshot1, snapshot2);
1635}
1636
1637
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001638HeapSnapshotGenerator::HeapSnapshotGenerator(HeapSnapshot* snapshot)
1639 : snapshot_(snapshot) {
1640}
1641
1642
1643void HeapSnapshotGenerator::GenerateSnapshot() {
1644 AssertNoAllocation no_alloc;
1645
1646 // Iterate heap contents.
1647 HeapIterator iterator;
1648 for (HeapObject* obj = iterator.next(); obj != NULL; obj = iterator.next()) {
1649 ExtractReferences(obj);
1650 }
1651
1652 snapshot_->CutObjectsFromForeignSecurityContexts();
1653}
1654
1655
1656void HeapSnapshotGenerator::ExtractReferences(HeapObject* obj) {
1657 HeapEntry* entry = snapshot_->GetEntry(obj);
1658 if (entry == NULL) return;
1659 if (entry->visited()) return;
1660
1661 if (obj->IsJSObject()) {
1662 JSObject* js_obj = JSObject::cast(obj);
1663 ExtractClosureReferences(js_obj, entry);
1664 ExtractPropertyReferences(js_obj, entry);
1665 ExtractElementReferences(js_obj, entry);
1666 snapshot_->SetPropertyReference(
1667 entry, Heap::prototype_symbol(), js_obj->map()->prototype());
1668 } else if (obj->IsJSGlobalPropertyCell()) {
1669 JSGlobalPropertyCell* cell = JSGlobalPropertyCell::cast(obj);
1670 snapshot_->SetElementReference(entry, 0, cell->value());
1671 } else if (obj->IsString()) {
1672 if (obj->IsConsString()) {
1673 ConsString* cs = ConsString::cast(obj);
1674 snapshot_->SetElementReference(entry, 0, cs->first());
1675 snapshot_->SetElementReference(entry, 1, cs->second());
1676 }
1677 } else if (obj->IsCode() || obj->IsSharedFunctionInfo() || obj->IsScript()) {
1678 IndexedReferencesExtractor refs_extractor(snapshot_, entry);
1679 obj->Iterate(&refs_extractor);
1680 } else if (obj->IsFixedArray()) {
1681 IndexedReferencesExtractor refs_extractor(snapshot_, entry);
1682 obj->Iterate(&refs_extractor);
1683 }
1684 entry->MarkAsVisited();
1685}
1686
1687
1688void HeapSnapshotGenerator::ExtractClosureReferences(JSObject* js_obj,
1689 HeapEntry* entry) {
1690 if (js_obj->IsJSFunction()) {
1691 HandleScope hs;
1692 JSFunction* func = JSFunction::cast(js_obj);
1693 Context* context = func->context();
1694 ZoneScope zscope(DELETE_ON_EXIT);
ager@chromium.orgb5737492010-07-15 09:29:43 +00001695 SerializedScopeInfo* serialized_scope_info =
1696 context->closure()->shared()->scope_info();
1697 ScopeInfo<ZoneListAllocationPolicy> zone_scope_info(serialized_scope_info);
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001698 int locals_number = zone_scope_info.NumberOfLocals();
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001699 for (int i = 0; i < locals_number; ++i) {
ager@chromium.org6a2b0aa2010-07-13 20:58:03 +00001700 String* local_name = *zone_scope_info.LocalName(i);
ager@chromium.orgb5737492010-07-15 09:29:43 +00001701 int idx = serialized_scope_info->ContextSlotIndex(local_name, NULL);
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001702 if (idx >= 0 && idx < context->length()) {
1703 snapshot_->SetClosureReference(entry, local_name, context->get(idx));
1704 }
1705 }
ricow@chromium.org5ad5ace2010-06-23 09:06:43 +00001706 snapshot_->SetInternalReference(entry, "code", func->shared());
ager@chromium.org2cc82ae2010-06-14 07:35:38 +00001707 }
1708}
1709
1710
1711void HeapSnapshotGenerator::ExtractPropertyReferences(JSObject* js_obj,
1712 HeapEntry* entry) {
1713 if (js_obj->HasFastProperties()) {
1714 DescriptorArray* descs = js_obj->map()->instance_descriptors();
1715 for (int i = 0; i < descs->number_of_descriptors(); i++) {
1716 switch (descs->GetType(i)) {
1717 case FIELD: {
1718 int index = descs->GetFieldIndex(i);
1719 snapshot_->SetPropertyReference(
1720 entry, descs->GetKey(i), js_obj->FastPropertyAt(index));
1721 break;
1722 }
1723 case CONSTANT_FUNCTION:
1724 snapshot_->SetPropertyReference(
1725 entry, descs->GetKey(i), descs->GetConstantFunction(i));
1726 break;
1727 default: ;
1728 }
1729 }
1730 } else {
1731 StringDictionary* dictionary = js_obj->property_dictionary();
1732 int length = dictionary->Capacity();
1733 for (int i = 0; i < length; ++i) {
1734 Object* k = dictionary->KeyAt(i);
1735 if (dictionary->IsKey(k)) {
1736 snapshot_->SetPropertyReference(
1737 entry, String::cast(k), dictionary->ValueAt(i));
1738 }
1739 }
1740 }
1741}
1742
1743
1744void HeapSnapshotGenerator::ExtractElementReferences(JSObject* js_obj,
1745 HeapEntry* entry) {
1746 if (js_obj->HasFastElements()) {
1747 FixedArray* elements = FixedArray::cast(js_obj->elements());
1748 int length = js_obj->IsJSArray() ?
1749 Smi::cast(JSArray::cast(js_obj)->length())->value() :
1750 elements->length();
1751 for (int i = 0; i < length; ++i) {
1752 if (!elements->get(i)->IsTheHole()) {
1753 snapshot_->SetElementReference(entry, i, elements->get(i));
1754 }
1755 }
1756 } else if (js_obj->HasDictionaryElements()) {
1757 NumberDictionary* dictionary = js_obj->element_dictionary();
1758 int length = dictionary->Capacity();
1759 for (int i = 0; i < length; ++i) {
1760 Object* k = dictionary->KeyAt(i);
1761 if (dictionary->IsKey(k)) {
1762 ASSERT(k->IsNumber());
1763 uint32_t index = static_cast<uint32_t>(k->Number());
1764 snapshot_->SetElementReference(entry, index, dictionary->ValueAt(i));
1765 }
1766 }
1767 }
1768}
1769
ricow@chromium.org4980dff2010-07-19 08:33:45 +00001770
1771static void DeleteHeapSnapshotsDiff(HeapSnapshotsDiff** diff_ptr) {
1772 delete *diff_ptr;
1773}
1774
1775HeapSnapshotsComparator::~HeapSnapshotsComparator() {
1776 diffs_.Iterate(DeleteHeapSnapshotsDiff);
1777}
1778
1779
1780HeapSnapshotsDiff* HeapSnapshotsComparator::Compare(HeapSnapshot* snapshot1,
1781 HeapSnapshot* snapshot2) {
1782 HeapSnapshotsDiff* diff = new HeapSnapshotsDiff(snapshot1, snapshot2);
1783 diffs_.Add(diff);
1784 List<HeapEntry*>* entries1 = snapshot1->GetSortedEntriesList();
1785 List<HeapEntry*>* entries2 = snapshot2->GetSortedEntriesList();
1786 int i = 0, j = 0;
1787 List<HeapEntry*> added_entries, deleted_entries;
1788 while (i < entries1->length() && j < entries2->length()) {
1789 uint64_t id1 = entries1->at(i)->id();
1790 uint64_t id2 = entries2->at(j)->id();
1791 if (id1 == id2) {
1792 i++;
1793 j++;
1794 } else if (id1 < id2) {
1795 HeapEntry* entry = entries1->at(i++);
1796 deleted_entries.Add(entry);
1797 } else {
1798 HeapEntry* entry = entries2->at(j++);
1799 added_entries.Add(entry);
1800 }
1801 }
1802 while (i < entries1->length()) {
1803 HeapEntry* entry = entries1->at(i++);
1804 deleted_entries.Add(entry);
1805 }
1806 while (j < entries2->length()) {
1807 HeapEntry* entry = entries2->at(j++);
1808 added_entries.Add(entry);
1809 }
1810
1811 snapshot1->ClearPaint();
1812 snapshot1->root()->PaintAllReachable();
1813 for (int i = 0; i < deleted_entries.length(); ++i) {
1814 HeapEntry* entry = deleted_entries[i];
1815 if (entry->painted_reachable())
1816 diff->AddDeletedEntry(entry);
1817 }
1818 snapshot2->ClearPaint();
1819 snapshot2->root()->PaintAllReachable();
1820 for (int i = 0; i < added_entries.length(); ++i) {
1821 HeapEntry* entry = added_entries[i];
1822 if (entry->painted_reachable())
1823 diff->AddAddedEntry(entry);
1824 }
1825 return diff;
1826}
1827
fschneider@chromium.org086aac62010-03-17 13:18:24 +00001828} } // namespace v8::internal
lrn@chromium.org25156de2010-04-06 13:10:27 +00001829
ricow@chromium.orgc9c80822010-04-21 08:22:37 +00001830#endif // ENABLE_LOGGING_AND_PROFILING