diff options
Diffstat (limited to 'Source/JavaScriptCore/profiler')
-rw-r--r-- | Source/JavaScriptCore/profiler/CallIdentifier.h | 100 | ||||
-rw-r--r-- | Source/JavaScriptCore/profiler/Profile.cpp | 136 | ||||
-rw-r--r-- | Source/JavaScriptCore/profiler/Profile.h | 72 | ||||
-rw-r--r-- | Source/JavaScriptCore/profiler/ProfileGenerator.cpp | 181 | ||||
-rw-r--r-- | Source/JavaScriptCore/profiler/ProfileGenerator.h | 79 | ||||
-rw-r--r-- | Source/JavaScriptCore/profiler/ProfileNode.cpp | 351 | ||||
-rw-r--r-- | Source/JavaScriptCore/profiler/ProfileNode.h | 172 | ||||
-rw-r--r-- | Source/JavaScriptCore/profiler/Profiler.cpp | 172 | ||||
-rw-r--r-- | Source/JavaScriptCore/profiler/Profiler.h | 77 | ||||
-rw-r--r-- | Source/JavaScriptCore/profiler/ProfilerServer.h | 35 | ||||
-rw-r--r-- | Source/JavaScriptCore/profiler/ProfilerServer.mm | 115 |
11 files changed, 1490 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/profiler/CallIdentifier.h b/Source/JavaScriptCore/profiler/CallIdentifier.h new file mode 100644 index 0000000..76c1470 --- /dev/null +++ b/Source/JavaScriptCore/profiler/CallIdentifier.h @@ -0,0 +1,100 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. 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. + */ + + +#ifndef CallIdentifier_h +#define CallIdentifier_h + +#include <runtime/UString.h> +#include "FastAllocBase.h" +#include <wtf/text/CString.h> +#include <wtf/text/StringHash.h> + +namespace JSC { + + struct CallIdentifier : public FastAllocBase { + UString m_name; + UString m_url; + unsigned m_lineNumber; + + CallIdentifier() + : m_lineNumber(0) + { + } + + CallIdentifier(const UString& name, const UString& url, int lineNumber) + : m_name(name) + , m_url(!url.isNull() ? url : "") + , m_lineNumber(lineNumber) + { + } + + inline bool operator==(const CallIdentifier& ci) const { return ci.m_lineNumber == m_lineNumber && ci.m_name == m_name && ci.m_url == m_url; } + inline bool operator!=(const CallIdentifier& ci) const { return !(*this == ci); } + + struct Hash { + static unsigned hash(const CallIdentifier& key) + { + unsigned hashCodes[3] = { + key.m_name.impl()->hash(), + key.m_url.impl()->hash(), + key.m_lineNumber + }; + return WTF::StringHasher::createBlobHash<sizeof(hashCodes)>(hashCodes); + } + + static bool equal(const CallIdentifier& a, const CallIdentifier& b) { return a == b; } + static const bool safeToCompareToEmptyOrDeleted = true; + }; + + unsigned hash() const { return Hash::hash(*this); } + +#ifndef NDEBUG + operator const char*() const { return c_str(); } + const char* c_str() const { return m_name.utf8().data(); } +#endif + }; + +} // namespace JSC + +namespace WTF { + + template<> struct DefaultHash<JSC::CallIdentifier> { typedef JSC::CallIdentifier::Hash Hash; }; + + template<> struct HashTraits<JSC::CallIdentifier> : GenericHashTraits<JSC::CallIdentifier> { + static void constructDeletedValue(JSC::CallIdentifier& slot) + { + new (&slot) JSC::CallIdentifier(JSC::UString(), JSC::UString(), std::numeric_limits<unsigned>::max()); + } + static bool isDeletedValue(const JSC::CallIdentifier& value) + { + return value.m_name.isNull() && value.m_url.isNull() && value.m_lineNumber == std::numeric_limits<unsigned>::max(); + } + }; + +} // namespace WTF + +#endif // CallIdentifier_h + diff --git a/Source/JavaScriptCore/profiler/Profile.cpp b/Source/JavaScriptCore/profiler/Profile.cpp new file mode 100644 index 0000000..1a84518 --- /dev/null +++ b/Source/JavaScriptCore/profiler/Profile.cpp @@ -0,0 +1,136 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. 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 "config.h" +#include "Profile.h" + +#include "ProfileNode.h" +#include <stdio.h> + +namespace JSC { + +PassRefPtr<Profile> Profile::create(const UString& title, unsigned uid) +{ + return adoptRef(new Profile(title, uid)); +} + +Profile::Profile(const UString& title, unsigned uid) + : m_title(title) + , m_uid(uid) +{ + // FIXME: When multi-threading is supported this will be a vector and calls + // into the profiler will need to know which thread it is executing on. + m_head = ProfileNode::create(0, CallIdentifier("Thread_1", UString(), 0), 0, 0); +} + +Profile::~Profile() +{ +} + +void Profile::forEach(void (ProfileNode::*function)()) +{ + ProfileNode* currentNode = m_head->firstChild(); + for (ProfileNode* nextNode = currentNode; nextNode; nextNode = nextNode->firstChild()) + currentNode = nextNode; + + if (!currentNode) + currentNode = m_head.get(); + + ProfileNode* endNode = m_head->traverseNextNodePostOrder(); + while (currentNode && currentNode != endNode) { + (currentNode->*function)(); + currentNode = currentNode->traverseNextNodePostOrder(); + } +} + +void Profile::focus(const ProfileNode* profileNode) +{ + if (!profileNode || !m_head) + return; + + bool processChildren; + const CallIdentifier& callIdentifier = profileNode->callIdentifier(); + for (ProfileNode* currentNode = m_head.get(); currentNode; currentNode = currentNode->traverseNextNodePreOrder(processChildren)) + processChildren = currentNode->focus(callIdentifier); + + // Set the visible time of all nodes so that the %s display correctly. + forEach(&ProfileNode::calculateVisibleTotalTime); +} + +void Profile::exclude(const ProfileNode* profileNode) +{ + if (!profileNode || !m_head) + return; + + const CallIdentifier& callIdentifier = profileNode->callIdentifier(); + + for (ProfileNode* currentNode = m_head.get(); currentNode; currentNode = currentNode->traverseNextNodePreOrder()) + currentNode->exclude(callIdentifier); + + // Set the visible time of the head so the %s display correctly. + m_head->setVisibleTotalTime(m_head->totalTime() - m_head->selfTime()); + m_head->setVisibleSelfTime(0.0); +} + +void Profile::restoreAll() +{ + forEach(&ProfileNode::restore); +} + +#ifndef NDEBUG +void Profile::debugPrintData() const +{ + printf("Call graph:\n"); + m_head->debugPrintData(0); +} + +typedef pair<StringImpl*, unsigned> NameCountPair; + +static inline bool functionNameCountPairComparator(const NameCountPair& a, const NameCountPair& b) +{ + return a.second > b.second; +} + +void Profile::debugPrintDataSampleStyle() const +{ + typedef Vector<NameCountPair> NameCountPairVector; + + FunctionCallHashCount countedFunctions; + printf("Call graph:\n"); + m_head->debugPrintDataSampleStyle(0, countedFunctions); + + printf("\nTotal number in stack:\n"); + NameCountPairVector sortedFunctions(countedFunctions.size()); + copyToVector(countedFunctions, sortedFunctions); + + std::sort(sortedFunctions.begin(), sortedFunctions.end(), functionNameCountPairComparator); + for (NameCountPairVector::iterator it = sortedFunctions.begin(); it != sortedFunctions.end(); ++it) + printf(" %-12d%s\n", (*it).second, UString((*it).first).utf8().data()); + + printf("\nSort by top of stack, same collapsed (when >= 5):\n"); +} +#endif + +} // namespace JSC diff --git a/Source/JavaScriptCore/profiler/Profile.h b/Source/JavaScriptCore/profiler/Profile.h new file mode 100644 index 0000000..6bf29f7 --- /dev/null +++ b/Source/JavaScriptCore/profiler/Profile.h @@ -0,0 +1,72 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. 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. + */ + +#ifndef Profile_h +#define Profile_h + +#include "ProfileNode.h" +#include <runtime/UString.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace JSC { + + class Profile : public RefCounted<Profile> { + public: + static PassRefPtr<Profile> create(const UString& title, unsigned uid); + virtual ~Profile(); + + const UString& title() const { return m_title; } + ProfileNode* head() const { return m_head.get(); } + void setHead(PassRefPtr<ProfileNode> head) { m_head = head; } + double totalTime() const { return m_head->totalTime(); } + unsigned int uid() const { return m_uid; } + + void forEach(void (ProfileNode::*)()); + + void focus(const ProfileNode*); + void exclude(const ProfileNode*); + void restoreAll(); + +#ifndef NDEBUG + void debugPrintData() const; + void debugPrintDataSampleStyle() const; +#endif + + protected: + Profile(const UString& title, unsigned uid); + + private: + void removeProfileStart(); + void removeProfileEnd(); + + UString m_title; + RefPtr<ProfileNode> m_head; + unsigned int m_uid; + }; + +} // namespace JSC + +#endif // Profile_h diff --git a/Source/JavaScriptCore/profiler/ProfileGenerator.cpp b/Source/JavaScriptCore/profiler/ProfileGenerator.cpp new file mode 100644 index 0000000..68d1733 --- /dev/null +++ b/Source/JavaScriptCore/profiler/ProfileGenerator.cpp @@ -0,0 +1,181 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. 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 "config.h" +#include "ProfileGenerator.h" + +#include "CallFrame.h" +#include "CodeBlock.h" +#include "JSGlobalObject.h" +#include "JSStringRef.h" +#include "JSFunction.h" +#include "Interpreter.h" +#include "Profile.h" +#include "Profiler.h" +#include "Tracing.h" + +namespace JSC { + +static const char* NonJSExecution = "(idle)"; + +PassRefPtr<ProfileGenerator> ProfileGenerator::create(const UString& title, ExecState* originatingExec, unsigned uid) +{ + return adoptRef(new ProfileGenerator(title, originatingExec, uid)); +} + +ProfileGenerator::ProfileGenerator(const UString& title, ExecState* originatingExec, unsigned uid) + : m_originatingGlobalExec(originatingExec ? originatingExec->lexicalGlobalObject()->globalExec() : 0) + , m_profileGroup(originatingExec ? originatingExec->lexicalGlobalObject()->profileGroup() : 0) +{ + m_profile = Profile::create(title, uid); + m_currentNode = m_head = m_profile->head(); + if (originatingExec) + addParentForConsoleStart(originatingExec); +} + +void ProfileGenerator::addParentForConsoleStart(ExecState* exec) +{ + int lineNumber; + intptr_t sourceID; + UString sourceURL; + JSValue function; + + exec->interpreter()->retrieveLastCaller(exec, lineNumber, sourceID, sourceURL, function); + m_currentNode = ProfileNode::create(exec, Profiler::createCallIdentifier(exec, function ? function.toThisObject(exec) : 0, sourceURL, lineNumber), m_head.get(), m_head.get()); + m_head->insertNode(m_currentNode.get()); +} + +const UString& ProfileGenerator::title() const +{ + return m_profile->title(); +} + +void ProfileGenerator::willExecute(ExecState* callerCallFrame, const CallIdentifier& callIdentifier) +{ + if (JAVASCRIPTCORE_PROFILE_WILL_EXECUTE_ENABLED()) { + CString name = callIdentifier.m_name.utf8(); + CString url = callIdentifier.m_url.utf8(); + JAVASCRIPTCORE_PROFILE_WILL_EXECUTE(m_profileGroup, const_cast<char*>(name.data()), const_cast<char*>(url.data()), callIdentifier.m_lineNumber); + } + + if (!m_originatingGlobalExec) + return; + + ASSERT(m_currentNode); + m_currentNode = m_currentNode->willExecute(callerCallFrame, callIdentifier); +} + +void ProfileGenerator::didExecute(ExecState* callerCallFrame, const CallIdentifier& callIdentifier) +{ + if (JAVASCRIPTCORE_PROFILE_DID_EXECUTE_ENABLED()) { + CString name = callIdentifier.m_name.utf8(); + CString url = callIdentifier.m_url.utf8(); + JAVASCRIPTCORE_PROFILE_DID_EXECUTE(m_profileGroup, const_cast<char*>(name.data()), const_cast<char*>(url.data()), callIdentifier.m_lineNumber); + } + + if (!m_originatingGlobalExec) + return; + + ASSERT(m_currentNode); + if (m_currentNode->callIdentifier() != callIdentifier) { + RefPtr<ProfileNode> returningNode = ProfileNode::create(callerCallFrame, callIdentifier, m_head.get(), m_currentNode.get()); + returningNode->setStartTime(m_currentNode->startTime()); + returningNode->didExecute(); + m_currentNode->insertNode(returningNode.release()); + return; + } + + m_currentNode = m_currentNode->didExecute(); +} + +void ProfileGenerator::exceptionUnwind(ExecState* handlerCallFrame, const CallIdentifier&) +{ + // If the current node was called by the handler (==) or any + // more nested function (>) the we have exited early from it. + ASSERT(m_currentNode); + while (m_currentNode->callerCallFrame() >= handlerCallFrame) { + didExecute(m_currentNode->callerCallFrame(), m_currentNode->callIdentifier()); + ASSERT(m_currentNode); + } +} + +void ProfileGenerator::stopProfiling() +{ + m_profile->forEach(&ProfileNode::stopProfiling); + + removeProfileStart(); + removeProfileEnd(); + + ASSERT(m_currentNode); + + // Set the current node to the parent, because we are in a call that + // will not get didExecute call. + m_currentNode = m_currentNode->parent(); + + if (double headSelfTime = m_head->selfTime()) { + RefPtr<ProfileNode> idleNode = ProfileNode::create(0, CallIdentifier(NonJSExecution, UString(), 0), m_head.get(), m_head.get()); + + idleNode->setTotalTime(headSelfTime); + idleNode->setSelfTime(headSelfTime); + idleNode->setVisible(true); + + m_head->setSelfTime(0.0); + m_head->addChild(idleNode.release()); + } +} + +// The console.ProfileGenerator that started this ProfileGenerator will be the first child. +void ProfileGenerator::removeProfileStart() +{ + ProfileNode* currentNode = 0; + for (ProfileNode* next = m_head.get(); next; next = next->firstChild()) + currentNode = next; + + if (currentNode->callIdentifier().m_name != "profile") + return; + + // Attribute the time of the node aobut to be removed to the self time of its parent + currentNode->parent()->setSelfTime(currentNode->parent()->selfTime() + currentNode->totalTime()); + currentNode->parent()->removeChild(currentNode); +} + +// The console.ProfileGeneratorEnd that stopped this ProfileGenerator will be the last child. +void ProfileGenerator::removeProfileEnd() +{ + ProfileNode* currentNode = 0; + for (ProfileNode* next = m_head.get(); next; next = next->lastChild()) + currentNode = next; + + if (currentNode->callIdentifier().m_name != "profileEnd") + return; + + // Attribute the time of the node aobut to be removed to the self time of its parent + currentNode->parent()->setSelfTime(currentNode->parent()->selfTime() + currentNode->totalTime()); + + ASSERT(currentNode->callIdentifier() == (currentNode->parent()->children()[currentNode->parent()->children().size() - 1])->callIdentifier()); + currentNode->parent()->removeChild(currentNode); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/profiler/ProfileGenerator.h b/Source/JavaScriptCore/profiler/ProfileGenerator.h new file mode 100644 index 0000000..cbed73b --- /dev/null +++ b/Source/JavaScriptCore/profiler/ProfileGenerator.h @@ -0,0 +1,79 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. 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. + */ + +#ifndef ProfileGenerator_h +#define ProfileGenerator_h + +#include "Profile.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace JSC { + + class ExecState; + class Profile; + class ProfileNode; + class UString; + struct CallIdentifier; + + class ProfileGenerator : public RefCounted<ProfileGenerator> { + public: + static PassRefPtr<ProfileGenerator> create(const UString& title, ExecState* originatingExec, unsigned uid); + + // Members + const UString& title() const; + PassRefPtr<Profile> profile() const { return m_profile; } + ExecState* originatingGlobalExec() const { return m_originatingGlobalExec; } + unsigned profileGroup() const { return m_profileGroup; } + + // Collecting + void willExecute(ExecState* callerCallFrame, const CallIdentifier&); + void didExecute(ExecState* callerCallFrame, const CallIdentifier&); + + void exceptionUnwind(ExecState* handlerCallFrame, const CallIdentifier&); + + // Stopping Profiling + void stopProfiling(); + + typedef void (ProfileGenerator::*ProfileFunction)(ExecState* callerOrHandlerCallFrame, const CallIdentifier& callIdentifier); + + private: + ProfileGenerator(const UString& title, ExecState* originatingExec, unsigned uid); + void addParentForConsoleStart(ExecState*); + + void removeProfileStart(); + void removeProfileEnd(); + + RefPtr<Profile> m_profile; + ExecState* m_originatingGlobalExec; + unsigned m_profileGroup; + RefPtr<ProfileNode> m_head; + RefPtr<ProfileNode> m_currentNode; + }; + +} // namespace JSC + +#endif // ProfileGenerator_h diff --git a/Source/JavaScriptCore/profiler/ProfileNode.cpp b/Source/JavaScriptCore/profiler/ProfileNode.cpp new file mode 100644 index 0000000..8f20bbe --- /dev/null +++ b/Source/JavaScriptCore/profiler/ProfileNode.cpp @@ -0,0 +1,351 @@ +/* + * 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 <stdio.h> +#include <wtf/DateMath.h> +#include <wtf/text/StringHash.h> + +#if OS(WINDOWS) +#include <windows.h> +#endif + +using namespace WTF; + +namespace JSC { + +static double getCount() +{ +#if OS(WINDOWS) + static LARGE_INTEGER frequency; + if (!frequency.QuadPart) + QueryPerformanceFrequency(&frequency); + LARGE_INTEGER counter; + QueryPerformanceCounter(&counter); + return static_cast<double>(counter.QuadPart) / frequency.QuadPart; +#else + return currentTimeMS(); +#endif +} + +ProfileNode::ProfileNode(ExecState* callerCallFrame, const CallIdentifier& callIdentifier, ProfileNode* headNode, ProfileNode* parentNode) + : m_callerCallFrame(callerCallFrame) + , 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(ExecState* callerCallFrame, ProfileNode* headNode, ProfileNode* nodeToCopy) + : m_callerCallFrame(callerCallFrame) + , 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(ExecState* callerCallFrame, const CallIdentifier& callIdentifier) +{ + for (StackIterator currentChild = m_children.begin(); currentChild != m_children.end(); ++currentChild) { + if ((*currentChild)->callIdentifier() == callIdentifier) { + (*currentChild)->startTimer(); + return (*currentChild).get(); + } + } + + RefPtr<ProfileNode> newChild = ProfileNode::create(callerCallFrame, 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<ProfileNode> prpChild) +{ + RefPtr<ProfileNode> 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<ProfileNode> prpNode) +{ + RefPtr<ProfileNode> 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().utf8().data(), + m_numberOfCalls, m_actualSelfTime, selfPercent(), m_actualTotalTime, totalPercent(), + m_visibleSelfTime, m_visibleTotalTime, + (m_visible ? "True" : "False"), + m_nextSibling ? m_nextSibling->functionName().utf8().data() : ""); + + ++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().utf8().data(); + double sampleCount = m_actualTotalTime * 1000; + if (indentLevel) { + for (int i = 0; i < indentLevel; ++i) + printf(" "); + + countedFunctions.add(functionName().impl()); + + 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().utf8().data()); + } + + return m_actualTotalTime; +} +#endif + +} // namespace JSC diff --git a/Source/JavaScriptCore/profiler/ProfileNode.h b/Source/JavaScriptCore/profiler/ProfileNode.h new file mode 100644 index 0000000..ffe7b6f --- /dev/null +++ b/Source/JavaScriptCore/profiler/ProfileNode.h @@ -0,0 +1,172 @@ +/* + * 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. + */ + +#ifndef ProfileNode_h +#define ProfileNode_h + +#include "CallIdentifier.h" +#include <wtf/HashCountedSet.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace JSC { + + class ExecState; + class ProfileNode; + + typedef Vector<RefPtr<ProfileNode> >::const_iterator StackIterator; + typedef HashCountedSet<StringImpl*> FunctionCallHashCount; + + class ProfileNode : public RefCounted<ProfileNode> { + public: + static PassRefPtr<ProfileNode> create(ExecState* callerCallFrame, const CallIdentifier& callIdentifier, ProfileNode* headNode, ProfileNode* parentNode) + { + return adoptRef(new ProfileNode(callerCallFrame, callIdentifier, headNode, parentNode)); + } + static PassRefPtr<ProfileNode> create(ExecState* callerCallFrame, ProfileNode* headNode, ProfileNode* node) + { + return adoptRef(new ProfileNode(callerCallFrame, headNode, node)); + } + + bool operator==(ProfileNode* node) { return m_callIdentifier == node->callIdentifier(); } + + ProfileNode* willExecute(ExecState* callerCallFrame, const CallIdentifier&); + ProfileNode* didExecute(); + + void stopProfiling(); + + // CallIdentifier members + ExecState* callerCallFrame() const { return m_callerCallFrame; } + const CallIdentifier& callIdentifier() const { return m_callIdentifier; } + const UString& functionName() const { return m_callIdentifier.m_name; } + const UString& url() const { return m_callIdentifier.m_url; } + unsigned lineNumber() const { return m_callIdentifier.m_lineNumber; } + + // Relationships + ProfileNode* head() const { return m_head; } + void setHead(ProfileNode* head) { m_head = head; } + ProfileNode* parent() const { return m_parent; } + void setParent(ProfileNode* parent) { m_parent = parent; } + ProfileNode* nextSibling() const { return m_nextSibling; } + void setNextSibling(ProfileNode* nextSibling) { m_nextSibling = nextSibling; } + + // Time members + double startTime() const { return m_startTime; } + void setStartTime(double startTime) { m_startTime = startTime; } + double totalTime() const { return m_visibleTotalTime; } + double actualTotalTime() const { return m_actualTotalTime; } + void setTotalTime(double time) { m_actualTotalTime = time; m_visibleTotalTime = time; } + void setActualTotalTime(double time) { m_actualTotalTime = time; } + void setVisibleTotalTime(double time) { m_visibleTotalTime = time; } + double selfTime() const { return m_visibleSelfTime; } + double actualSelfTime() const { return m_actualSelfTime; } + void setSelfTime(double time) {m_actualSelfTime = time; m_visibleSelfTime = time; } + void setActualSelfTime(double time) { m_actualSelfTime = time; } + void setVisibleSelfTime(double time) { m_visibleSelfTime = time; } + + double totalPercent() const { return (m_visibleTotalTime / (m_head ? m_head->totalTime() : totalTime())) * 100.0; } + double selfPercent() const { return (m_visibleSelfTime / (m_head ? m_head->totalTime() : totalTime())) * 100.0; } + + unsigned numberOfCalls() const { return m_numberOfCalls; } + void setNumberOfCalls(unsigned number) { m_numberOfCalls = number; } + + // Children members + const Vector<RefPtr<ProfileNode> >& children() const { return m_children; } + ProfileNode* firstChild() const { return m_children.size() ? m_children.first().get() : 0; } + ProfileNode* lastChild() const { return m_children.size() ? m_children.last().get() : 0; } + ProfileNode* findChild(ProfileNode*) const; + void removeChild(ProfileNode*); + void addChild(PassRefPtr<ProfileNode> prpChild); + void insertNode(PassRefPtr<ProfileNode> prpNode); + + // Visiblity + bool visible() const { return m_visible; } + void setVisible(bool visible) { m_visible = visible; } + + static void setTreeVisible(ProfileNode*, bool visible); + + // Sorting + ProfileNode* traverseNextNodePostOrder() const; + ProfileNode* traverseNextNodePreOrder(bool processChildren = true) const; + + // Views + void calculateVisibleTotalTime(); + bool focus(const CallIdentifier&); + void exclude(const CallIdentifier&); + void restore(); + + void endAndRecordCall(); + +#ifndef NDEBUG + const char* c_str() const { return m_callIdentifier; } + void debugPrintData(int indentLevel) const; + double debugPrintDataSampleStyle(int indentLevel, FunctionCallHashCount&) const; +#endif + + private: + ProfileNode(ExecState* callerCallFrame, const CallIdentifier&, ProfileNode* headNode, ProfileNode* parentNode); + ProfileNode(ExecState* callerCallFrame, ProfileNode* headNode, ProfileNode* nodeToCopy); + + void startTimer(); + void resetChildrensSiblings(); + + RefPtr<ProfileNode>* childrenBegin() { return m_children.begin(); } + RefPtr<ProfileNode>* childrenEnd() { return m_children.end(); } + + // Sorting comparators + static inline bool totalTimeDescendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b) { return a->totalTime() > b->totalTime(); } + static inline bool totalTimeAscendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b) { return a->totalTime() < b->totalTime(); } + static inline bool selfTimeDescendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b) { return a->selfTime() > b->selfTime(); } + static inline bool selfTimeAscendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b) { return a->selfTime() < b->selfTime(); } + static inline bool callsDescendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b) { return a->numberOfCalls() > b->numberOfCalls(); } + static inline bool callsAscendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b) { return a->numberOfCalls() < b->numberOfCalls(); } + static inline bool functionNameDescendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b) { return a->functionName() > b->functionName(); } + static inline bool functionNameAscendingComparator(const RefPtr<ProfileNode>& a, const RefPtr<ProfileNode>& b) { return a->functionName() < b->functionName(); } + + ExecState* m_callerCallFrame; + CallIdentifier m_callIdentifier; + ProfileNode* m_head; + ProfileNode* m_parent; + ProfileNode* m_nextSibling; + + double m_startTime; + double m_actualTotalTime; + double m_visibleTotalTime; + double m_actualSelfTime; + double m_visibleSelfTime; + unsigned m_numberOfCalls; + + bool m_visible; + + Vector<RefPtr<ProfileNode> > m_children; + }; + +} // namespace JSC + +#endif // ProfileNode_h diff --git a/Source/JavaScriptCore/profiler/Profiler.cpp b/Source/JavaScriptCore/profiler/Profiler.cpp new file mode 100644 index 0000000..9ac73fd --- /dev/null +++ b/Source/JavaScriptCore/profiler/Profiler.cpp @@ -0,0 +1,172 @@ +/* + * 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 "Profiler.h" + +#include "CommonIdentifiers.h" +#include "CallFrame.h" +#include "CodeBlock.h" +#include "InternalFunction.h" +#include "JSFunction.h" +#include "JSGlobalObject.h" +#include "Nodes.h" +#include "Profile.h" +#include "ProfileGenerator.h" +#include "ProfileNode.h" +#include "UStringConcatenate.h" +#include <stdio.h> + +namespace JSC { + +static const char* GlobalCodeExecution = "(program)"; +static const char* AnonymousFunction = "(anonymous function)"; +static unsigned ProfilesUID = 0; + +static CallIdentifier createCallIdentifierFromFunctionImp(ExecState*, JSFunction*); + +Profiler* Profiler::s_sharedProfiler = 0; +Profiler* Profiler::s_sharedEnabledProfilerReference = 0; + +Profiler* Profiler::profiler() +{ + if (!s_sharedProfiler) + s_sharedProfiler = new Profiler(); + return s_sharedProfiler; +} + +void Profiler::startProfiling(ExecState* exec, const UString& title) +{ + ASSERT_ARG(title, !title.isNull()); + + // Check if we currently have a Profile for this global ExecState and title. + // If so return early and don't create a new Profile. + ExecState* globalExec = exec ? exec->lexicalGlobalObject()->globalExec() : 0; + + for (size_t i = 0; i < m_currentProfiles.size(); ++i) { + ProfileGenerator* profileGenerator = m_currentProfiles[i].get(); + if (profileGenerator->originatingGlobalExec() == globalExec && profileGenerator->title() == title) + return; + } + + s_sharedEnabledProfilerReference = this; + RefPtr<ProfileGenerator> profileGenerator = ProfileGenerator::create(title, exec, ++ProfilesUID); + m_currentProfiles.append(profileGenerator); +} + +PassRefPtr<Profile> Profiler::stopProfiling(ExecState* exec, const UString& title) +{ + ExecState* globalExec = exec ? exec->lexicalGlobalObject()->globalExec() : 0; + for (ptrdiff_t i = m_currentProfiles.size() - 1; i >= 0; --i) { + ProfileGenerator* profileGenerator = m_currentProfiles[i].get(); + if (profileGenerator->originatingGlobalExec() == globalExec && (title.isNull() || profileGenerator->title() == title)) { + profileGenerator->stopProfiling(); + RefPtr<Profile> returnProfile = profileGenerator->profile(); + + m_currentProfiles.remove(i); + if (!m_currentProfiles.size()) + s_sharedEnabledProfilerReference = 0; + + return returnProfile; + } + } + + return 0; +} + +static inline void dispatchFunctionToProfiles(ExecState* callerOrHandlerCallFrame, const Vector<RefPtr<ProfileGenerator> >& profiles, ProfileGenerator::ProfileFunction function, const CallIdentifier& callIdentifier, unsigned currentProfileTargetGroup) +{ + for (size_t i = 0; i < profiles.size(); ++i) { + if (profiles[i]->profileGroup() == currentProfileTargetGroup || !profiles[i]->originatingGlobalExec()) + (profiles[i].get()->*function)(callerOrHandlerCallFrame, callIdentifier); + } +} + +void Profiler::willExecute(ExecState* callerCallFrame, JSValue function) +{ + ASSERT(!m_currentProfiles.isEmpty()); + + dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::willExecute, createCallIdentifier(callerCallFrame, function, "", 0), callerCallFrame->lexicalGlobalObject()->profileGroup()); +} + +void Profiler::willExecute(ExecState* callerCallFrame, const UString& sourceURL, int startingLineNumber) +{ + ASSERT(!m_currentProfiles.isEmpty()); + + CallIdentifier callIdentifier = createCallIdentifier(callerCallFrame, JSValue(), sourceURL, startingLineNumber); + + dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::willExecute, callIdentifier, callerCallFrame->lexicalGlobalObject()->profileGroup()); +} + +void Profiler::didExecute(ExecState* callerCallFrame, JSValue function) +{ + ASSERT(!m_currentProfiles.isEmpty()); + + dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(callerCallFrame, function, "", 0), callerCallFrame->lexicalGlobalObject()->profileGroup()); +} + +void Profiler::didExecute(ExecState* callerCallFrame, const UString& sourceURL, int startingLineNumber) +{ + ASSERT(!m_currentProfiles.isEmpty()); + + dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(callerCallFrame, JSValue(), sourceURL, startingLineNumber), callerCallFrame->lexicalGlobalObject()->profileGroup()); +} + +void Profiler::exceptionUnwind(ExecState* handlerCallFrame) +{ + ASSERT(!m_currentProfiles.isEmpty()); + + dispatchFunctionToProfiles(handlerCallFrame, m_currentProfiles, &ProfileGenerator::exceptionUnwind, createCallIdentifier(handlerCallFrame, JSValue(), "", 0), handlerCallFrame->lexicalGlobalObject()->profileGroup()); +} + +CallIdentifier Profiler::createCallIdentifier(ExecState* exec, JSValue functionValue, const UString& defaultSourceURL, int defaultLineNumber) +{ + if (!functionValue) + return CallIdentifier(GlobalCodeExecution, defaultSourceURL, defaultLineNumber); + if (!functionValue.isObject()) + return CallIdentifier("(unknown)", defaultSourceURL, defaultLineNumber); + if (asObject(functionValue)->inherits(&JSFunction::info)) { + JSFunction* function = asFunction(functionValue); + if (!function->executable()->isHostFunction()) + return createCallIdentifierFromFunctionImp(exec, function); + } + if (asObject(functionValue)->inherits(&JSFunction::info)) + return CallIdentifier(static_cast<JSFunction*>(asObject(functionValue))->name(exec), defaultSourceURL, defaultLineNumber); + if (asObject(functionValue)->inherits(&InternalFunction::info)) + return CallIdentifier(static_cast<InternalFunction*>(asObject(functionValue))->name(exec), defaultSourceURL, defaultLineNumber); + return CallIdentifier(makeUString("(", asObject(functionValue)->className(), " object)"), defaultSourceURL, defaultLineNumber); +} + +CallIdentifier createCallIdentifierFromFunctionImp(ExecState* exec, JSFunction* function) +{ + ASSERT(!function->isHostFunction()); + const UString& name = function->calculatedDisplayName(exec); + return CallIdentifier(name.isEmpty() ? AnonymousFunction : name, function->jsExecutable()->sourceURL(), function->jsExecutable()->lineNo()); +} + +} // namespace JSC diff --git a/Source/JavaScriptCore/profiler/Profiler.h b/Source/JavaScriptCore/profiler/Profiler.h new file mode 100644 index 0000000..f9c2ccb --- /dev/null +++ b/Source/JavaScriptCore/profiler/Profiler.h @@ -0,0 +1,77 @@ +/* + * 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. + */ + +#ifndef Profiler_h +#define Profiler_h + +#include "Profile.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefPtr.h> +#include <wtf/Vector.h> + +namespace JSC { + + class ExecState; + class JSGlobalData; + class JSObject; + class JSValue; + class ProfileGenerator; + class UString; + struct CallIdentifier; + + class Profiler : public FastAllocBase { + public: + static Profiler** enabledProfilerReference() + { + return &s_sharedEnabledProfilerReference; + } + + static Profiler* profiler(); + static CallIdentifier createCallIdentifier(ExecState* exec, JSValue, const UString& sourceURL, int lineNumber); + + void startProfiling(ExecState*, const UString& title); + PassRefPtr<Profile> stopProfiling(ExecState*, const UString& title); + + void willExecute(ExecState* callerCallFrame, JSValue function); + void willExecute(ExecState* callerCallFrame, const UString& sourceURL, int startingLineNumber); + void didExecute(ExecState* callerCallFrame, JSValue function); + void didExecute(ExecState* callerCallFrame, const UString& sourceURL, int startingLineNumber); + + void exceptionUnwind(ExecState* handlerCallFrame); + + const Vector<RefPtr<ProfileGenerator> >& currentProfiles() { return m_currentProfiles; }; + + private: + Vector<RefPtr<ProfileGenerator> > m_currentProfiles; + static Profiler* s_sharedProfiler; + static Profiler* s_sharedEnabledProfilerReference; + }; + +} // namespace JSC + +#endif // Profiler_h diff --git a/Source/JavaScriptCore/profiler/ProfilerServer.h b/Source/JavaScriptCore/profiler/ProfilerServer.h new file mode 100644 index 0000000..5b7cc46 --- /dev/null +++ b/Source/JavaScriptCore/profiler/ProfilerServer.h @@ -0,0 +1,35 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. 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. + */ + +#ifndef ProfileServer_h +#define ProfileServer_h + +namespace JSC { + +void startProfilerServerIfNeeded(); + +} // namespace JSC + +#endif // ProfileServer_h diff --git a/Source/JavaScriptCore/profiler/ProfilerServer.mm b/Source/JavaScriptCore/profiler/ProfilerServer.mm new file mode 100644 index 0000000..7d87f96 --- /dev/null +++ b/Source/JavaScriptCore/profiler/ProfilerServer.mm @@ -0,0 +1,115 @@ +/* + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``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 INC. 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. + */ + +#import "config.h" +#import "ProfilerServer.h" + +#import "JSProfilerPrivate.h" +#import "JSRetainPtr.h" +#import <Foundation/Foundation.h> + +#if PLATFORM(IOS_SIMULATOR) +#import <Foundation/NSDistributedNotificationCenter.h> +#endif + +@interface ProfilerServer : NSObject { +@private + NSString *_serverName; + unsigned _listenerCount; +} ++ (ProfilerServer *)sharedProfileServer; +- (void)startProfiling; +- (void)stopProfiling; +@end + +@implementation ProfilerServer + ++ (ProfilerServer *)sharedProfileServer +{ + static ProfilerServer *sharedServer; + if (!sharedServer) + sharedServer = [[ProfilerServer alloc] init]; + return sharedServer; +} + +- (id)init +{ + if (!(self = [super init])) + return nil; + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + if ([defaults boolForKey:@"EnableJSProfiling"]) + [self startProfiling]; + +#if !PLATFORM(IOS) || PLATFORM(IOS_SIMULATOR) + // FIXME: <rdar://problem/6546135> + // The catch-all notifications + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(startProfiling) name:@"ProfilerServerStartNotification" object:nil]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(stopProfiling) name:@"ProfilerServerStopNotification" object:nil]; +#endif + + // The specific notifications + NSProcessInfo *processInfo = [NSProcessInfo processInfo]; + _serverName = [[NSString alloc] initWithFormat:@"ProfilerServer-%d", [processInfo processIdentifier]]; + +#if !PLATFORM(IOS) || PLATFORM(IOS_SIMULATOR) + // FIXME: <rdar://problem/6546135> + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(startProfiling) name:[_serverName stringByAppendingString:@"-Start"] object:nil]; + [[NSDistributedNotificationCenter defaultCenter] addObserver:self selector:@selector(stopProfiling) name:[_serverName stringByAppendingString:@"-Stop"] object:nil]; +#endif + + [pool drain]; + + return self; +} + +- (void)startProfiling +{ + if (++_listenerCount > 1) + return; + JSRetainPtr<JSStringRef> profileName(Adopt, JSStringCreateWithUTF8CString([_serverName UTF8String])); + JSStartProfiling(0, profileName.get()); +} + +- (void)stopProfiling +{ + if (!_listenerCount || --_listenerCount > 0) + return; + JSRetainPtr<JSStringRef> profileName(Adopt, JSStringCreateWithUTF8CString([_serverName UTF8String])); + JSEndProfiling(0, profileName.get()); +} + +@end + +namespace JSC { + +void startProfilerServerIfNeeded() +{ + [ProfilerServer sharedProfileServer]; +} + +} // namespace JSC |