Chromium Code Reviews| Index: src/profile-generator.cc |
| diff --git a/src/profile-generator.cc b/src/profile-generator.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..5c64d362c36268e5951d5ab2f43f15e5e0d9836f |
| --- /dev/null |
| +++ b/src/profile-generator.cc |
| @@ -0,0 +1,295 @@ |
| +// Copyright 2010 the V8 project authors. All rights reserved. |
| +// Redistribution and use in source and binary forms, with or without |
| +// modification, are permitted provided that the following conditions are |
| +// met: |
| +// |
| +// * Redistributions of source code must retain the above copyright |
| +// notice, this list of conditions and the following disclaimer. |
| +// * Redistributions in binary form must reproduce the above |
| +// copyright notice, this list of conditions and the following |
| +// disclaimer in the documentation and/or other materials provided |
| +// with the distribution. |
| +// * Neither the name of Google Inc. nor the names of its |
| +// contributors may be used to endorse or promote products derived |
| +// from this software without specific prior written permission. |
| +// |
| +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + |
| +#include "v8.h" |
| + |
| +#include "profile-generator-inl.h" |
| + |
| + |
| +namespace v8 { |
| +namespace internal { |
| + |
| + |
| +ProfileNode* ProfileNode::FindChild(CodeEntry* entry) { |
| + HashMap::Entry* map_entry = |
| + children_.Lookup(entry, CodeEntryHash(entry), false); |
| + return map_entry != NULL ? |
| + reinterpret_cast<ProfileNode*>(map_entry->value) : NULL; |
| +} |
| + |
| + |
| +ProfileNode* ProfileNode::FindOrAddChild(CodeEntry* entry) { |
| + HashMap::Entry* map_entry = |
| + children_.Lookup(entry, CodeEntryHash(entry), true); |
| + if (map_entry->value == NULL) { |
| + // New node added. |
| + map_entry->value = new ProfileNode(entry); |
| + } |
| + return reinterpret_cast<ProfileNode*>(map_entry->value); |
| +} |
| + |
| + |
| +void ProfileNode::Print(int indent) { |
| + OS::Print("%4u %4u %*c %s\n", |
| + total_ticks_, self_ticks_, |
| + indent, ' ', |
| + entry_ != NULL ? entry_->name() : ""); |
| + for (HashMap::Entry* p = children_.Start(); |
| + p != NULL; |
| + p = children_.Next(p)) { |
| + reinterpret_cast<ProfileNode*>(p->value)->Print(indent + 2); |
| + } |
| +} |
| + |
| + |
| +namespace { |
| + |
| +class DeleteNodesCallback { |
| + public: |
| + void AfterAllChildrenTraversed(ProfileNode* node) { |
| + delete node; |
| + } |
| + |
| + void AfterChildTraversed(ProfileNode*, ProfileNode*) { } |
| +}; |
| + |
| +} // namespace |
| + |
| + |
| +ProfileTree::~ProfileTree() { |
| + DeleteNodesCallback cb; |
| + TraverseBreadthFirstPostOrder(&cb); |
| +} |
| + |
| + |
| +void ProfileTree::AddPathFromEnd(const Vector<CodeEntry*>& path) { |
| + ProfileNode* node = root_; |
| + for (CodeEntry** entry = path.start() + path.length() - 1; |
| + entry != path.start() - 1; |
| + --entry) { |
| + if (*entry != NULL) { |
| + node = node->FindOrAddChild(*entry); |
| + } |
| + } |
| + node->IncrementSelfTicks(); |
| +} |
| + |
| + |
| +void ProfileTree::AddPathFromStart(const Vector<CodeEntry*>& path) { |
| + ProfileNode* node = root_; |
| + for (CodeEntry** entry = path.start(); |
| + entry != path.start() + path.length(); |
| + ++entry) { |
| + if (*entry != NULL) { |
| + node = node->FindOrAddChild(*entry); |
| + } |
| + } |
| + node->IncrementSelfTicks(); |
| +} |
| + |
| + |
| +namespace { |
| + |
| +struct Position { |
| + Position(ProfileNode* a_node, HashMap::Entry* a_p) |
| + : node(a_node), p(a_p) { } |
| + INLINE(ProfileNode* current_child()) { |
| + return reinterpret_cast<ProfileNode*>(p->value); |
| + } |
| + ProfileNode* node; |
| + HashMap::Entry* p; |
| +}; |
| + |
| +} // namespace |
| + |
| + |
| +template <typename Callback> |
| +void ProfileTree::TraverseBreadthFirstPostOrder(Callback* callback) { |
| + List<Position> stack(10); |
| + stack.Add(Position(root_, root_->children_.Start())); |
| + do { |
| + Position& current = stack.last(); |
| + if (current.p != NULL) { |
| + stack.Add(Position(current.current_child(), |
| + current.current_child()->children_.Start())); |
| + } else { |
| + callback->AfterAllChildrenTraversed(current.node); |
| + if (stack.length() > 1) { |
| + Position& parent = stack[stack.length() - 2]; |
| + callback->AfterChildTraversed(parent.node, current.node); |
|
Finnur
2011/03/25 15:18:48
If the callback is a DeleteNodesCallback, then we'
mnaganov (inactive)
2011/03/28 08:16:37
You are right. But in DeleteNodesCallback, AfterCh
|
| + parent.p = parent.node->children_.Next(parent.p); |
| + // Remove child from the stack. |
| + stack.RemoveLast(); |
| + } |
| + } |
| + } while (stack.length() > 1 || stack.last().p != NULL); |
| +} |
| + |
| + |
| +namespace { |
| + |
| +class CalculateTotalTicksCallback { |
| + public: |
| + void AfterAllChildrenTraversed(ProfileNode* node) { |
| + node->IncreaseTotalTicks(node->self_ticks()); |
| + } |
| + |
| + void AfterChildTraversed(ProfileNode* parent, ProfileNode* child) { |
| + parent->IncreaseTotalTicks(child->total_ticks()); |
| + } |
| +}; |
| + |
| +} // namespace |
| + |
| + |
| +// Non-recursive implementation of breadth-first tree traversal. |
| +void ProfileTree::CalculateTotalTicks() { |
| + CalculateTotalTicksCallback cb; |
| + TraverseBreadthFirstPostOrder(&cb); |
| +} |
| + |
| + |
| +void ProfileTree::ShortPrint() { |
| + OS::Print("root: %u %u\n", root_->total_ticks(), root_->self_ticks()); |
| +} |
| + |
| + |
| +void CpuProfile::AddPath(const Vector<CodeEntry*>& path) { |
| + top_down_.AddPathFromEnd(path); |
| + bottom_up_.AddPathFromStart(path); |
| +} |
| + |
| + |
| +void CpuProfile::CalculateTotalTicks() { |
| + top_down_.CalculateTotalTicks(); |
| + bottom_up_.CalculateTotalTicks(); |
| +} |
| + |
| + |
| +void CpuProfile::ShortPrint() { |
| + OS::Print("top down "); |
| + top_down_.ShortPrint(); |
| + OS::Print("bottom up "); |
| + bottom_up_.ShortPrint(); |
| +} |
| + |
| + |
| +void CpuProfile::Print() { |
| + OS::Print("[Top down]:\n"); |
| + top_down_.Print(); |
| + OS::Print("[Bottom up]:\n"); |
| + bottom_up_.Print(); |
| +} |
| + |
| + |
| +const CodeMap::CodeTreeConfig::Key CodeMap::CodeTreeConfig::kNoKey = NULL; |
| +const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue = |
| + CodeMap::CodeEntryInfo(NULL, 0); |
| + |
| + |
| +void CodeMap::AddAlias(Address alias, Address addr) { |
| + CodeTree::Locator locator; |
| + if (tree_.Find(addr, &locator)) { |
| + const CodeEntryInfo& entry_info = locator.value(); |
| + tree_.Insert(alias, &locator); |
| + locator.set_value(entry_info); |
| + } |
| +} |
| + |
| + |
| +CodeEntry* CodeMap::FindEntry(Address addr) { |
| + CodeTree::Locator locator; |
| + if (tree_.FindGreatestLessThan(addr, &locator)) { |
| + // locator.key() <= addr. Need to check that addr is within entry. |
| + const CodeEntryInfo& entry = locator.value(); |
| + if (addr < (locator.key() + entry.size)) |
| + return entry.entry; |
| + } |
| + return NULL; |
| +} |
| + |
| + |
| +ProfileGenerator::ProfileGenerator() |
| + : resource_names_(StringsMatch) { |
| +} |
| + |
| + |
| +static void CodeEntriesDeleter(CodeEntry** entry_ptr) { |
| + delete *entry_ptr; |
| +} |
| + |
| + |
| +ProfileGenerator::~ProfileGenerator() { |
| + for (HashMap::Entry* p = resource_names_.Start(); |
| + p != NULL; |
| + p = resource_names_.Next(p)) { |
| + DeleteArray(reinterpret_cast<const char*>(p->value)); |
| + } |
| + |
| + code_entries_.Iterate(CodeEntriesDeleter); |
| +} |
| + |
| + |
| +CodeEntry* ProfileGenerator::NewCodeEntry( |
| + Logger::LogEventsAndTags tag, |
| + String* name, |
| + String* resource_name, int line_number) { |
| + const char* cached_resource_name = NULL; |
| + if (resource_name->IsString()) { |
| + // As we copy contents of resource names, and usually they are repeated, |
| + // we cache names by string hashcode. |
| + HashMap::Entry* cache_entry = |
| + resource_names_.Lookup(resource_name, |
| + StringEntryHash(resource_name), |
| + true); |
| + if (cache_entry->value == NULL) { |
| + // New entry added. |
| + cache_entry->value = |
| + resource_name->ToCString(DISALLOW_NULLS, |
| + ROBUST_STRING_TRAVERSAL).Detach(); |
| + } |
| + cached_resource_name = reinterpret_cast<const char*>(cache_entry->value); |
| + } |
| + |
| + CodeEntry* entry = new ManagedNameCodeEntry(tag, |
| + name, |
| + cached_resource_name, |
| + line_number); |
| + code_entries_.Add(entry); |
| + return entry; |
| +} |
| + |
| + |
| +CodeEntry* ProfileGenerator::NewCodeEntry( |
| + Logger::LogEventsAndTags tag, |
| + const char* name) { |
| + CodeEntry* entry = new StaticNameCodeEntry(tag, name); |
| + code_entries_.Add(entry); |
| + return entry; |
| +} |
| + |
| +} } // namespace v8::internal |