/* * Copyright (C) 2008 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. 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. * 3. Neither the name of Apple Computer, Inc. ("Apple") 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 APPLE AND ITS 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 APPLE OR ITS 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 "config.h" #include "ProfileNode.h" #include "Profiler.h" #include #include #if OS(WINDOWS) #include #endif using namespace WTF; namespace JSC { static double getCount() { #if OS(WINDOWS) static LARGE_INTEGER frequency = {0}; if (!frequency.QuadPart) QueryPerformanceFrequency(&frequency); LARGE_INTEGER counter; QueryPerformanceCounter(&counter); return static_cast(counter.QuadPart) / frequency.QuadPart; #else return currentTimeMS(); #endif } ProfileNode::ProfileNode(const CallIdentifier& callIdentifier, ProfileNode* headNode, ProfileNode* parentNode) : m_callIdentifier(callIdentifier) , m_head(headNode) , m_parent(parentNode) , m_nextSibling(0) , m_startTime(0.0) , m_actualTotalTime(0.0) , m_visibleTotalTime(0.0) , m_actualSelfTime(0.0) , m_visibleSelfTime(0.0) , m_numberOfCalls(0) , m_visible(true) { startTimer(); } ProfileNode::ProfileNode(ProfileNode* headNode, ProfileNode* nodeToCopy) : m_callIdentifier(nodeToCopy->callIdentifier()) , m_head(headNode) , m_parent(nodeToCopy->parent()) , m_nextSibling(0) , m_startTime(0.0) , m_actualTotalTime(nodeToCopy->actualTotalTime()) , m_visibleTotalTime(nodeToCopy->totalTime()) , m_actualSelfTime(nodeToCopy->actualSelfTime()) , m_visibleSelfTime(nodeToCopy->selfTime()) , m_numberOfCalls(nodeToCopy->numberOfCalls()) , m_visible(nodeToCopy->visible()) { } ProfileNode* ProfileNode::willExecute(const CallIdentifier& callIdentifier) { for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) { if ((*currentChild)->callIdentifier() == callIdentifier) { (*currentChild)->startTimer(); return (*currentChild).get(); } } RefPtr newChild = ProfileNode::create(callIdentifier, m_head ? m_head : this, this); // If this ProfileNode has no head it is the head. if (m_children.size()) m_children.last()->setNextSibling(newChild.get()); m_children.append(newChild.release()); return m_children.last().get(); } ProfileNode* ProfileNode::didExecute() { endAndRecordCall(); return m_parent; } void ProfileNode::addChild(PassRefPtr prpChild) { RefPtr child = prpChild; child->setParent(this); if (m_children.size()) m_children.last()->setNextSibling(child.get()); m_children.append(child.release()); } ProfileNode* ProfileNode::findChild(ProfileNode* node) const { if (!node) return 0; for (size_t i = 0; i < m_children.size(); ++i) { if (*node == m_children[i].get()) return m_children[i].get(); } return 0; } void ProfileNode::removeChild(ProfileNode* node) { if (!node) return; for (size_t i = 0; i < m_children.size(); ++i) { if (*node == m_children[i].get()) { m_children.remove(i); break; } } resetChildrensSiblings(); } void ProfileNode::insertNode(PassRefPtr prpNode) { RefPtr node = prpNode; for (unsigned i = 0; i < m_children.size(); ++i) node->addChild(m_children[i].release()); m_children.clear(); m_children.append(node.release()); } void ProfileNode::stopProfiling() { if (m_startTime) endAndRecordCall(); m_visibleTotalTime = m_actualTotalTime; ASSERT(m_actualSelfTime == 0.0 && m_startTime == 0.0); // Because we iterate in post order all of our children have been stopped before us. for (unsigned i = 0; i < m_children.size(); ++i) m_actualSelfTime += m_children[i]->totalTime(); ASSERT(m_actualSelfTime <= m_actualTotalTime); m_actualSelfTime = m_actualTotalTime - m_actualSelfTime; m_visibleSelfTime = m_actualSelfTime; } ProfileNode* ProfileNode::traverseNextNodePostOrder() const { ProfileNode* next = m_nextSibling; if (!next) return m_parent; while (ProfileNode* firstChild = next->firstChild()) next = firstChild; return next; } ProfileNode* ProfileNode::traverseNextNodePreOrder(bool processChildren) const { if (processChildren && m_children.size()) return m_children[0].get(); if (m_nextSibling) return m_nextSibling; ProfileNode* nextParent = m_parent; if (!nextParent) return 0; ProfileNode* next; for (next = m_parent->nextSibling(); !next; next = nextParent->nextSibling()) { nextParent = nextParent->parent(); if (!nextParent) return 0; } return next; } void ProfileNode::setTreeVisible(ProfileNode* node, bool visible) { ProfileNode* nodeParent = node->parent(); ProfileNode* nodeSibling = node->nextSibling(); node->setParent(0); node->setNextSibling(0); for (ProfileNode* currentNode = node; currentNode; currentNode = currentNode->traverseNextNodePreOrder()) currentNode->setVisible(visible); node->setParent(nodeParent); node->setNextSibling(nodeSibling); } void ProfileNode::calculateVisibleTotalTime() { double sumOfVisibleChildrensTime = 0.0; for (unsigned i = 0; i < m_children.size(); ++i) { if (m_children[i]->visible()) sumOfVisibleChildrensTime += m_children[i]->totalTime(); } m_visibleTotalTime = m_visibleSelfTime + sumOfVisibleChildrensTime; } bool ProfileNode::focus(const CallIdentifier& callIdentifier) { if (!m_visible) return false; if (m_callIdentifier != callIdentifier) { m_visible = false; return true; } for (ProfileNode* currentParent = m_parent; currentParent; currentParent = currentParent->parent()) currentParent->setVisible(true); return false; } void ProfileNode::exclude(const CallIdentifier& callIdentifier) { if (m_visible && m_callIdentifier == callIdentifier) { setTreeVisible(this, false); m_parent->setVisibleSelfTime(m_parent->selfTime() + m_visibleTotalTime); } } void ProfileNode::restore() { m_visibleTotalTime = m_actualTotalTime; m_visibleSelfTime = m_actualSelfTime; m_visible = true; } void ProfileNode::endAndRecordCall() { m_actualTotalTime += m_startTime ? getCount() - m_startTime : 0.0; m_startTime = 0.0; ++m_numberOfCalls; } void ProfileNode::startTimer() { if (!m_startTime) m_startTime = getCount(); } void ProfileNode::resetChildrensSiblings() { unsigned size = m_children.size(); for (unsigned i = 0; i < size; ++i) m_children[i]->setNextSibling(i + 1 == size ? 0 : m_children[i + 1].get()); } #ifndef NDEBUG void ProfileNode::debugPrintData(int indentLevel) const { // Print function names for (int i = 0; i < indentLevel; ++i) printf(" "); printf("Function Name %s %d SelfTime %.3fms/%.3f%% TotalTime %.3fms/%.3f%% VSelf %.3fms VTotal %.3fms Visible %s Next Sibling %s\n", functionName().UTF8String().c_str(), m_numberOfCalls, m_actualSelfTime, selfPercent(), m_actualTotalTime, totalPercent(), m_visibleSelfTime, m_visibleTotalTime, (m_visible ? "True" : "False"), m_nextSibling ? m_nextSibling->functionName().UTF8String().c_str() : ""); ++indentLevel; // Print children's names and information for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) (*currentChild)->debugPrintData(indentLevel); } // print the profiled data in a format that matches the tool sample's output. double ProfileNode::debugPrintDataSampleStyle(int indentLevel, FunctionCallHashCount& countedFunctions) const { printf(" "); // Print function names const char* name = functionName().UTF8String().c_str(); double sampleCount = m_actualTotalTime * 1000; if (indentLevel) { for (int i = 0; i < indentLevel; ++i) printf(" "); countedFunctions.add(functionName().rep()); printf("%.0f %s\n", sampleCount ? sampleCount : 1, name); } else printf("%s\n", name); ++indentLevel; // Print children's names and information double sumOfChildrensCount = 0.0; for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) sumOfChildrensCount += (*currentChild)->debugPrintDataSampleStyle(indentLevel, countedFunctions); sumOfChildrensCount *= 1000; // // Print remainder of samples to match sample's output if (sumOfChildrensCount < sampleCount) { printf(" "); while (indentLevel--) printf(" "); printf("%.0f %s\n", sampleCount - sumOfChildrensCount, functionName().UTF8String().c_str()); } return m_actualTotalTime; } #endif } // namespace JSC