1/* 2 * Copyright (C) 2008, 2014 Apple Inc. All Rights Reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "ProfileGenerator.h" 28 29#include "CallFrame.h" 30#include "CodeBlock.h" 31#include "JSGlobalObject.h" 32#include "JSStringRef.h" 33#include "JSFunction.h" 34#include "LegacyProfiler.h" 35#include "JSCInlines.h" 36#include "Profile.h" 37#include "StackVisitor.h" 38#include "Tracing.h" 39 40namespace JSC { 41 42PassRefPtr<ProfileGenerator> ProfileGenerator::create(ExecState* exec, const String& title, unsigned uid) 43{ 44 return adoptRef(new ProfileGenerator(exec, title, uid)); 45} 46 47ProfileGenerator::ProfileGenerator(ExecState* exec, const String& title, unsigned uid) 48 : m_origin(exec ? exec->lexicalGlobalObject() : 0) 49 , m_profileGroup(exec ? exec->lexicalGlobalObject()->profileGroup() : 0) 50 , m_foundConsoleStartParent(false) 51{ 52 m_profile = Profile::create(title, uid); 53 m_currentNode = m_head = m_profile->head(); 54 if (exec) 55 addParentForConsoleStart(exec); 56} 57 58class AddParentForConsoleStartFunctor { 59public: 60 AddParentForConsoleStartFunctor(ExecState* exec, RefPtr<ProfileNode>& head, RefPtr<ProfileNode>& currentNode) 61 : m_exec(exec) 62 , m_hasSkippedFirstFrame(false) 63 , m_foundParent(false) 64 , m_head(head) 65 , m_currentNode(currentNode) 66 { 67 } 68 69 bool foundParent() const { return m_foundParent; } 70 71 StackVisitor::Status operator()(StackVisitor& visitor) 72 { 73 if (!m_hasSkippedFirstFrame) { 74 m_hasSkippedFirstFrame = true; 75 return StackVisitor::Continue; 76 } 77 78 unsigned line = 0; 79 unsigned column = 0; 80 visitor->computeLineAndColumn(line, column); 81 m_currentNode = ProfileNode::create(m_exec, LegacyProfiler::createCallIdentifier(m_exec, visitor->callee(), visitor->sourceURL(), line, column), m_head.get(), m_head.get()); 82 m_head->insertNode(m_currentNode.get()); 83 84 m_foundParent = true; 85 return StackVisitor::Done; 86 } 87 88private: 89 ExecState* m_exec; 90 bool m_hasSkippedFirstFrame; 91 bool m_foundParent; 92 RefPtr<ProfileNode>& m_head; 93 RefPtr<ProfileNode>& m_currentNode; 94}; 95 96void ProfileGenerator::addParentForConsoleStart(ExecState* exec) 97{ 98 AddParentForConsoleStartFunctor functor(exec, m_head, m_currentNode); 99 exec->iterate(functor); 100 101 m_foundConsoleStartParent = functor.foundParent(); 102} 103 104const String& ProfileGenerator::title() const 105{ 106 return m_profile->title(); 107} 108 109void ProfileGenerator::willExecute(ExecState* callerCallFrame, const CallIdentifier& callIdentifier) 110{ 111 if (JAVASCRIPTCORE_PROFILE_WILL_EXECUTE_ENABLED()) { 112 CString name = callIdentifier.functionName().utf8(); 113 CString url = callIdentifier.url().utf8(); 114 JAVASCRIPTCORE_PROFILE_WILL_EXECUTE(m_profileGroup, const_cast<char*>(name.data()), const_cast<char*>(url.data()), callIdentifier.lineNumber()); 115 } 116 117 if (!m_origin) 118 return; 119 120 ASSERT(m_currentNode); 121 m_currentNode = m_currentNode->willExecute(callerCallFrame, callIdentifier); 122} 123 124void ProfileGenerator::didExecute(ExecState* callerCallFrame, const CallIdentifier& callIdentifier) 125{ 126 if (JAVASCRIPTCORE_PROFILE_DID_EXECUTE_ENABLED()) { 127 CString name = callIdentifier.functionName().utf8(); 128 CString url = callIdentifier.url().utf8(); 129 JAVASCRIPTCORE_PROFILE_DID_EXECUTE(m_profileGroup, const_cast<char*>(name.data()), const_cast<char*>(url.data()), callIdentifier.lineNumber()); 130 } 131 132 if (!m_origin) 133 return; 134 135 ASSERT(m_currentNode); 136 if (m_currentNode->callIdentifier() != callIdentifier) { 137 RefPtr<ProfileNode> returningNode = ProfileNode::create(callerCallFrame, callIdentifier, m_head.get(), m_currentNode.get()); 138 returningNode->lastCall().setStartTime(m_currentNode->lastCall().startTime()); 139 returningNode->didExecute(); 140 m_currentNode->insertNode(returningNode.release()); 141 return; 142 } 143 144 m_currentNode = m_currentNode->didExecute(); 145} 146 147void ProfileGenerator::exceptionUnwind(ExecState* handlerCallFrame, const CallIdentifier&) 148{ 149 // If the current node was called by the handler (==) or any 150 // more nested function (>) the we have exited early from it. 151 ASSERT(m_currentNode); 152 while (m_currentNode->callerCallFrame() >= handlerCallFrame) { 153 didExecute(m_currentNode->callerCallFrame(), m_currentNode->callIdentifier()); 154 ASSERT(m_currentNode); 155 } 156} 157 158void ProfileGenerator::stopProfiling() 159{ 160 m_profile->forEach(&ProfileNode::stopProfiling); 161 162 if (m_foundConsoleStartParent) { 163 removeProfileStart(); 164 removeProfileEnd(); 165 } 166 167 ASSERT(m_currentNode); 168 169 // Set the current node to the parent, because we are in a call that 170 // will not get didExecute call. 171 m_currentNode = m_currentNode->parent(); 172 173 if (double headSelfTime = m_head->selfTime()) { 174 m_head->setSelfTime(0.0); 175 m_profile->setIdleTime(headSelfTime); 176 } 177} 178 179// The console.profile that started this ProfileGenerator will be the first child. 180void ProfileGenerator::removeProfileStart() 181{ 182 ProfileNode* currentNode = 0; 183 for (ProfileNode* next = m_head.get(); next; next = next->firstChild()) 184 currentNode = next; 185 186 if (currentNode->callIdentifier().functionName() != "profile") 187 return; 188 189 // Attribute the time of the node aobut to be removed to the self time of its parent 190 currentNode->parent()->setSelfTime(currentNode->parent()->selfTime() + currentNode->totalTime()); 191 currentNode->parent()->removeChild(currentNode); 192} 193 194// The console.profileEnd that stopped this ProfileGenerator will be the last child. 195void ProfileGenerator::removeProfileEnd() 196{ 197 ProfileNode* currentNode = 0; 198 for (ProfileNode* next = m_head.get(); next; next = next->lastChild()) 199 currentNode = next; 200 201 if (currentNode->callIdentifier().functionName() != "profileEnd") 202 return; 203 204 // Attribute the time of the node aobut to be removed to the self time of its parent 205 currentNode->parent()->setSelfTime(currentNode->parent()->selfTime() + currentNode->totalTime()); 206 207 ASSERT(currentNode->callIdentifier() == (currentNode->parent()->children()[currentNode->parent()->children().size() - 1])->callIdentifier()); 208 currentNode->parent()->removeChild(currentNode); 209} 210 211} // namespace JSC 212