2 * Copyright (C) 2008, 2014 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "ProfileGenerator.h"
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"
37 #include "StackVisitor.h"
42 Ref
<ProfileGenerator
> ProfileGenerator::create(ExecState
* exec
, const String
& title
, unsigned uid
, PassRefPtr
<Stopwatch
> stopwatch
)
44 return adoptRef(*new ProfileGenerator(exec
, title
, uid
, stopwatch
));
47 ProfileGenerator::ProfileGenerator(ExecState
* exec
, const String
& title
, unsigned uid
, PassRefPtr
<Stopwatch
> stopwatch
)
48 : m_origin(exec
? exec
->lexicalGlobalObject() : nullptr)
49 , m_profileGroup(exec
? exec
->lexicalGlobalObject()->profileGroup() : 0)
50 , m_stopwatch(stopwatch
)
51 , m_foundConsoleStartParent(false)
54 double startTime
= m_stopwatch
->elapsedTime();
55 m_profile
= Profile::create(title
, uid
, startTime
);
56 m_currentNode
= m_rootNode
= m_profile
->rootNode();
58 addParentForConsoleStart(exec
, startTime
);
61 class AddParentForConsoleStartFunctor
{
63 AddParentForConsoleStartFunctor(ExecState
* exec
, RefPtr
<ProfileNode
>& rootNode
, RefPtr
<ProfileNode
>& currentNode
, double startTime
)
65 , m_hasSkippedFirstFrame(false)
66 , m_foundParent(false)
67 , m_rootNode(rootNode
)
68 , m_currentNode(currentNode
)
69 , m_startTime(startTime
)
73 bool foundParent() const { return m_foundParent
; }
75 StackVisitor::Status
operator()(StackVisitor
& visitor
)
77 if (!m_hasSkippedFirstFrame
) {
78 m_hasSkippedFirstFrame
= true;
79 return StackVisitor::Continue
;
84 visitor
->computeLineAndColumn(line
, column
);
85 m_currentNode
= ProfileNode::create(m_exec
, LegacyProfiler::createCallIdentifier(m_exec
, visitor
->callee(), visitor
->sourceURL(), line
, column
), m_rootNode
.get());
86 m_currentNode
->appendCall(ProfileNode::Call(m_startTime
));
87 m_rootNode
->spliceNode(m_currentNode
.get());
90 return StackVisitor::Done
;
95 bool m_hasSkippedFirstFrame
;
97 RefPtr
<ProfileNode
>& m_rootNode
;
98 RefPtr
<ProfileNode
>& m_currentNode
;
102 void ProfileGenerator::addParentForConsoleStart(ExecState
* exec
, double startTime
)
104 AddParentForConsoleStartFunctor
functor(exec
, m_rootNode
, m_currentNode
, startTime
);
105 exec
->iterate(functor
);
107 m_foundConsoleStartParent
= functor
.foundParent();
110 const String
& ProfileGenerator::title() const
112 return m_profile
->title();
115 void ProfileGenerator::beginCallEntry(ProfileNode
* node
, double startTime
)
117 ASSERT_ARG(node
, node
);
119 if (std::isnan(startTime
))
120 startTime
= m_stopwatch
->elapsedTime();
122 node
->appendCall(ProfileNode::Call(startTime
));
125 void ProfileGenerator::endCallEntry(ProfileNode
* node
)
127 ASSERT_ARG(node
, node
);
129 ProfileNode::Call
& last
= node
->lastCall();
131 double previousElapsedTime
= std::isnan(last
.elapsedTime()) ? 0.0 : last
.elapsedTime();
132 double newlyElapsedTime
= m_stopwatch
->elapsedTime() - last
.startTime();
133 last
.setElapsedTime(previousElapsedTime
+ newlyElapsedTime
);
136 void ProfileGenerator::willExecute(ExecState
* callerCallFrame
, const CallIdentifier
& callIdentifier
)
138 if (JAVASCRIPTCORE_PROFILE_WILL_EXECUTE_ENABLED()) {
139 CString name
= callIdentifier
.functionName().utf8();
140 CString url
= callIdentifier
.url().utf8();
141 JAVASCRIPTCORE_PROFILE_WILL_EXECUTE(m_profileGroup
, const_cast<char*>(name
.data()), const_cast<char*>(url
.data()), callIdentifier
.lineNumber(), callIdentifier
.columnNumber());
150 RefPtr
<ProfileNode
> calleeNode
= nullptr;
152 // Find or create a node for the callee call frame.
153 for (const RefPtr
<ProfileNode
>& child
: m_currentNode
->children()) {
154 if (child
->callIdentifier() == callIdentifier
)
159 calleeNode
= ProfileNode::create(callerCallFrame
, callIdentifier
, m_currentNode
.get());
160 m_currentNode
->addChild(calleeNode
);
163 m_currentNode
= calleeNode
;
164 beginCallEntry(calleeNode
.get(), m_stopwatch
->elapsedTime());
167 void ProfileGenerator::didExecute(ExecState
* callerCallFrame
, const CallIdentifier
& callIdentifier
)
169 if (JAVASCRIPTCORE_PROFILE_DID_EXECUTE_ENABLED()) {
170 CString name
= callIdentifier
.functionName().utf8();
171 CString url
= callIdentifier
.url().utf8();
172 JAVASCRIPTCORE_PROFILE_DID_EXECUTE(m_profileGroup
, const_cast<char*>(name
.data()), const_cast<char*>(url
.data()), callIdentifier
.lineNumber(), callIdentifier
.columnNumber());
181 // Make a new node if the caller node has never seen this callee call frame before.
182 // This can happen if |console.profile()| is called several frames deep in the call stack.
183 ASSERT(m_currentNode
);
184 if (m_currentNode
->callIdentifier() != callIdentifier
) {
185 RefPtr
<ProfileNode
> calleeNode
= ProfileNode::create(callerCallFrame
, callIdentifier
, m_currentNode
.get());
186 beginCallEntry(calleeNode
.get(), m_currentNode
->lastCall().startTime());
187 endCallEntry(calleeNode
.get());
188 m_currentNode
->spliceNode(calleeNode
.release());
192 endCallEntry(m_currentNode
.get());
193 m_currentNode
= m_currentNode
->parent();
196 void ProfileGenerator::exceptionUnwind(ExecState
* handlerCallFrame
, const CallIdentifier
&)
201 // If the current node was called by the handler (==) or any
202 // more nested function (>) the we have exited early from it.
203 ASSERT(m_currentNode
);
204 while (m_currentNode
->callerCallFrame() >= handlerCallFrame
) {
205 didExecute(m_currentNode
->callerCallFrame(), m_currentNode
->callIdentifier());
206 ASSERT(m_currentNode
);
210 void ProfileGenerator::stopProfiling()
212 for (ProfileNode
* node
= m_currentNode
.get(); node
!= m_profile
->rootNode(); node
= node
->parent())
215 if (m_foundConsoleStartParent
) {
216 removeProfileStart();
220 ASSERT(m_currentNode
);
222 // Set the current node to the parent, because we are in a call that
223 // will not get didExecute call.
224 m_currentNode
= m_currentNode
->parent();
227 // The console.profile that started this ProfileGenerator will be the first child.
228 void ProfileGenerator::removeProfileStart()
230 ProfileNode
* currentNode
= nullptr;
231 for (ProfileNode
* next
= m_rootNode
.get(); next
; next
= next
->firstChild())
234 if (currentNode
->callIdentifier().functionName() != "profile")
237 currentNode
->parent()->removeChild(currentNode
);
240 // The console.profileEnd that stopped this ProfileGenerator will be the last child.
241 void ProfileGenerator::removeProfileEnd()
243 ProfileNode
* currentNode
= nullptr;
244 for (ProfileNode
* next
= m_rootNode
.get(); next
; next
= next
->lastChild())
247 if (currentNode
->callIdentifier().functionName() != "profileEnd")
250 ASSERT(currentNode
->callIdentifier() == (currentNode
->parent()->children()[currentNode
->parent()->children().size() - 1])->callIdentifier());
251 currentNode
->parent()->removeChild(currentNode
);