1/* 2 * Copyright (C) 2008, 2012, 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 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. Neither the name of Apple Inc. ("Apple") nor the names of 14 * its contributors may be used to endorse or promote products derived 15 * from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30#include "LegacyProfiler.h" 31 32#include "CallFrame.h" 33#include "CodeBlock.h" 34#include "CommonIdentifiers.h" 35#include "InternalFunction.h" 36#include "JSFunction.h" 37#include "JSGlobalObject.h" 38#include "Nodes.h" 39#include "JSCInlines.h" 40#include "Profile.h" 41#include "ProfileGenerator.h" 42#include "ProfileNode.h" 43 44namespace JSC { 45 46static const char* GlobalCodeExecution = "(program)"; 47static const char* AnonymousFunction = "(anonymous function)"; 48static unsigned ProfilesUID = 0; 49 50static CallIdentifier createCallIdentifierFromFunctionImp(ExecState*, JSObject*, const String& defaultSourceURL, unsigned defaultLineNumber, unsigned defaultColumnNumber); 51 52LegacyProfiler* LegacyProfiler::s_sharedLegacyProfiler = 0; 53 54LegacyProfiler* LegacyProfiler::profiler() 55{ 56 if (!s_sharedLegacyProfiler) 57 s_sharedLegacyProfiler = new LegacyProfiler(); 58 return s_sharedLegacyProfiler; 59} 60 61void LegacyProfiler::startProfiling(ExecState* exec, const String& title) 62{ 63 if (!exec) 64 return; 65 66 // Check if we currently have a Profile for this global ExecState and title. 67 // If so return early and don't create a new Profile. 68 JSGlobalObject* origin = exec->lexicalGlobalObject(); 69 70 for (size_t i = 0; i < m_currentProfiles.size(); ++i) { 71 ProfileGenerator* profileGenerator = m_currentProfiles[i].get(); 72 if (profileGenerator->origin() == origin && profileGenerator->title() == title) 73 return; 74 } 75 76 exec->vm().setEnabledProfiler(this); 77 RefPtr<ProfileGenerator> profileGenerator = ProfileGenerator::create(exec, title, ++ProfilesUID); 78 m_currentProfiles.append(profileGenerator); 79} 80 81PassRefPtr<Profile> LegacyProfiler::stopProfiling(ExecState* exec, const String& title) 82{ 83 if (!exec) 84 return 0; 85 86 JSGlobalObject* origin = exec->lexicalGlobalObject(); 87 for (ptrdiff_t i = m_currentProfiles.size() - 1; i >= 0; --i) { 88 ProfileGenerator* profileGenerator = m_currentProfiles[i].get(); 89 if (profileGenerator->origin() == origin && (title.isNull() || profileGenerator->title() == title)) { 90 profileGenerator->stopProfiling(); 91 RefPtr<Profile> returnProfile = profileGenerator->profile(); 92 93 m_currentProfiles.remove(i); 94 if (!m_currentProfiles.size()) 95 exec->vm().setEnabledProfiler(nullptr); 96 97 return returnProfile; 98 } 99 } 100 101 return 0; 102} 103 104void LegacyProfiler::stopProfiling(JSGlobalObject* origin) 105{ 106 for (ptrdiff_t i = m_currentProfiles.size() - 1; i >= 0; --i) { 107 ProfileGenerator* profileGenerator = m_currentProfiles[i].get(); 108 if (profileGenerator->origin() == origin) { 109 profileGenerator->stopProfiling(); 110 m_currentProfiles.remove(i); 111 if (!m_currentProfiles.size()) 112 origin->vm().setEnabledProfiler(nullptr); 113 } 114 } 115} 116 117static inline void dispatchFunctionToProfiles(ExecState* callerOrHandlerCallFrame, const Vector<RefPtr<ProfileGenerator>>& profiles, ProfileGenerator::ProfileFunction function, const CallIdentifier& callIdentifier, unsigned currentProfileTargetGroup) 118{ 119 for (size_t i = 0; i < profiles.size(); ++i) { 120 if (profiles[i]->profileGroup() == currentProfileTargetGroup || !profiles[i]->origin()) 121 (profiles[i].get()->*function)(callerOrHandlerCallFrame, callIdentifier); 122 } 123} 124 125void LegacyProfiler::willExecute(ExecState* callerCallFrame, JSValue function) 126{ 127 ASSERT(!m_currentProfiles.isEmpty()); 128 129 dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::willExecute, createCallIdentifier(callerCallFrame, function, StringImpl::empty(), 0, 0), callerCallFrame->lexicalGlobalObject()->profileGroup()); 130} 131 132void LegacyProfiler::willExecute(ExecState* callerCallFrame, const String& sourceURL, unsigned startingLineNumber, unsigned startingColumnNumber) 133{ 134 ASSERT(!m_currentProfiles.isEmpty()); 135 136 CallIdentifier callIdentifier = createCallIdentifier(callerCallFrame, JSValue(), sourceURL, startingLineNumber, startingColumnNumber); 137 138 dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::willExecute, callIdentifier, callerCallFrame->lexicalGlobalObject()->profileGroup()); 139} 140 141void LegacyProfiler::didExecute(ExecState* callerCallFrame, JSValue function) 142{ 143 ASSERT(!m_currentProfiles.isEmpty()); 144 145 dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(callerCallFrame, function, StringImpl::empty(), 0, 0), callerCallFrame->lexicalGlobalObject()->profileGroup()); 146} 147 148void LegacyProfiler::didExecute(ExecState* callerCallFrame, const String& sourceURL, unsigned startingLineNumber, unsigned startingColumnNumber) 149{ 150 ASSERT(!m_currentProfiles.isEmpty()); 151 152 dispatchFunctionToProfiles(callerCallFrame, m_currentProfiles, &ProfileGenerator::didExecute, createCallIdentifier(callerCallFrame, JSValue(), sourceURL, startingLineNumber, startingColumnNumber), callerCallFrame->lexicalGlobalObject()->profileGroup()); 153} 154 155void LegacyProfiler::exceptionUnwind(ExecState* handlerCallFrame) 156{ 157 ASSERT(!m_currentProfiles.isEmpty()); 158 159 dispatchFunctionToProfiles(handlerCallFrame, m_currentProfiles, &ProfileGenerator::exceptionUnwind, createCallIdentifier(handlerCallFrame, JSValue(), StringImpl::empty(), 0, 0), handlerCallFrame->lexicalGlobalObject()->profileGroup()); 160} 161 162CallIdentifier LegacyProfiler::createCallIdentifier(ExecState* exec, JSValue functionValue, const String& defaultSourceURL, unsigned defaultLineNumber, unsigned defaultColumnNumber) 163{ 164 if (!functionValue) 165 return CallIdentifier(ASCIILiteral(GlobalCodeExecution), defaultSourceURL, defaultLineNumber, defaultColumnNumber); 166 if (!functionValue.isObject()) 167 return CallIdentifier(ASCIILiteral("(unknown)"), defaultSourceURL, defaultLineNumber, defaultColumnNumber); 168 if (asObject(functionValue)->inherits(JSFunction::info()) || asObject(functionValue)->inherits(InternalFunction::info())) 169 return createCallIdentifierFromFunctionImp(exec, asObject(functionValue), defaultSourceURL, defaultLineNumber, defaultColumnNumber); 170 return CallIdentifier(asObject(functionValue)->methodTable()->className(asObject(functionValue)), defaultSourceURL, defaultLineNumber, defaultColumnNumber); 171} 172 173CallIdentifier createCallIdentifierFromFunctionImp(ExecState* exec, JSObject* function, const String& defaultSourceURL, unsigned defaultLineNumber, unsigned defaultColumnNumber) 174{ 175 const String& name = getCalculatedDisplayName(exec, function); 176 JSFunction* jsFunction = jsDynamicCast<JSFunction*>(function); 177 if (jsFunction && !jsFunction->isHostOrBuiltinFunction()) 178 return CallIdentifier(name.isEmpty() ? ASCIILiteral(AnonymousFunction) : name, jsFunction->jsExecutable()->sourceURL(), jsFunction->jsExecutable()->lineNo(), jsFunction->jsExecutable()->startColumn()); 179 return CallIdentifier(name.isEmpty() ? ASCIILiteral(AnonymousFunction) : name, defaultSourceURL, defaultLineNumber, defaultColumnNumber); 180} 181 182} // namespace JSC 183