• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 Google 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 are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "config.h"
32 #include "core/inspector/AsyncCallStackTracker.h"
33 
34 #include "bindings/v8/V8RecursionScope.h"
35 #include "core/dom/ContextLifecycleObserver.h"
36 #include "core/dom/ExecutionContext.h"
37 #include "core/events/EventTarget.h"
38 #include "core/xml/XMLHttpRequest.h"
39 #include "core/xml/XMLHttpRequestUpload.h"
40 #include "wtf/text/AtomicStringHash.h"
41 #include "wtf/text/StringBuilder.h"
42 #include <v8.h>
43 
44 namespace {
45 
46 static const char setTimeoutName[] = "setTimeout";
47 static const char setIntervalName[] = "setInterval";
48 static const char requestAnimationFrameName[] = "requestAnimationFrame";
49 static const char xhrSendName[] = "XMLHttpRequest.send";
50 static const char enqueueMutationRecordName[] = "Mutation";
51 
52 }
53 
54 namespace WebCore {
55 
56 class AsyncCallStackTracker::ExecutionContextData FINAL : public ContextLifecycleObserver {
57     WTF_MAKE_FAST_ALLOCATED;
58 public:
ExecutionContextData(AsyncCallStackTracker * tracker,ExecutionContext * executionContext)59     ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* executionContext)
60         : ContextLifecycleObserver(executionContext)
61         , m_tracker(tracker)
62     {
63     }
64 
contextDestroyed()65     virtual void contextDestroyed() OVERRIDE
66     {
67         ASSERT(executionContext());
68         ExecutionContextData* self = m_tracker->m_executionContextDataMap.take(executionContext());
69         ASSERT(self == this);
70         ContextLifecycleObserver::contextDestroyed();
71         delete self;
72     }
73 
74 public:
75     AsyncCallStackTracker* m_tracker;
76     HashSet<int> m_intervalTimerIds;
77     HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains;
78     HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains;
79     HashMap<Event*, RefPtr<AsyncCallChain> > m_eventCallChains;
80     HashMap<EventTarget*, RefPtr<AsyncCallChain> > m_xhrCallChains;
81     HashMap<MutationObserver*, RefPtr<AsyncCallChain> > m_mutationObserverCallChains;
82 };
83 
toXmlHttpRequest(EventTarget * eventTarget)84 static XMLHttpRequest* toXmlHttpRequest(EventTarget* eventTarget)
85 {
86     const AtomicString& interfaceName = eventTarget->interfaceName();
87     if (interfaceName == EventTargetNames::XMLHttpRequest)
88         return static_cast<XMLHttpRequest*>(eventTarget);
89     if (interfaceName == EventTargetNames::XMLHttpRequestUpload)
90         return static_cast<XMLHttpRequestUpload*>(eventTarget)->xmlHttpRequest();
91     return 0;
92 }
93 
AsyncCallStack(const String & description,const ScriptValue & callFrames)94 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames)
95     : m_description(description)
96     , m_callFrames(callFrames)
97 {
98 }
99 
~AsyncCallStack()100 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack()
101 {
102 }
103 
AsyncCallStackTracker()104 AsyncCallStackTracker::AsyncCallStackTracker()
105     : m_maxAsyncCallStackDepth(0)
106 {
107 }
108 
setAsyncCallStackDepth(int depth)109 void AsyncCallStackTracker::setAsyncCallStackDepth(int depth)
110 {
111     if (depth <= 0) {
112         m_maxAsyncCallStackDepth = 0;
113         clear();
114     } else {
115         m_maxAsyncCallStackDepth = depth;
116     }
117 }
118 
currentAsyncCallChain() const119 const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsyncCallChain() const
120 {
121     if (m_currentAsyncCallChain)
122         ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCallStackDepth);
123     return m_currentAsyncCallChain.get();
124 }
125 
didInstallTimer(ExecutionContext * context,int timerId,bool singleShot,const ScriptValue & callFrames)126 void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timerId, bool singleShot, const ScriptValue& callFrames)
127 {
128     ASSERT(context);
129     ASSERT(isEnabled());
130     if (!validateCallFrames(callFrames))
131         return;
132     ASSERT(timerId > 0);
133     ExecutionContextData* data = createContextDataIfNeeded(context);
134     data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTimeoutName : setIntervalName, callFrames));
135     if (!singleShot)
136         data->m_intervalTimerIds.add(timerId);
137 }
138 
didRemoveTimer(ExecutionContext * context,int timerId)139 void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerId)
140 {
141     ASSERT(context);
142     ASSERT(isEnabled());
143     if (timerId <= 0)
144         return;
145     ExecutionContextData* data = m_executionContextDataMap.get(context);
146     if (!data)
147         return;
148     data->m_intervalTimerIds.remove(timerId);
149     data->m_timerCallChains.remove(timerId);
150 }
151 
willFireTimer(ExecutionContext * context,int timerId)152 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId)
153 {
154     ASSERT(context);
155     ASSERT(isEnabled());
156     ASSERT(timerId > 0);
157     ASSERT(!m_currentAsyncCallChain);
158     if (ExecutionContextData* data = m_executionContextDataMap.get(context)) {
159         if (data->m_intervalTimerIds.contains(timerId))
160             setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId));
161         else
162             setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId));
163     } else {
164         setCurrentAsyncCallChain(nullptr);
165     }
166 }
167 
didRequestAnimationFrame(ExecutionContext * context,int callbackId,const ScriptValue & callFrames)168 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames)
169 {
170     ASSERT(context);
171     ASSERT(isEnabled());
172     if (!validateCallFrames(callFrames))
173         return;
174     ASSERT(callbackId > 0);
175     ExecutionContextData* data = createContextDataIfNeeded(context);
176     data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(requestAnimationFrameName, callFrames));
177 }
178 
didCancelAnimationFrame(ExecutionContext * context,int callbackId)179 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, int callbackId)
180 {
181     ASSERT(context);
182     ASSERT(isEnabled());
183     if (callbackId <= 0)
184         return;
185     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
186         data->m_animationFrameCallChains.remove(callbackId);
187 }
188 
willFireAnimationFrame(ExecutionContext * context,int callbackId)189 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, int callbackId)
190 {
191     ASSERT(context);
192     ASSERT(isEnabled());
193     ASSERT(callbackId > 0);
194     ASSERT(!m_currentAsyncCallChain);
195     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
196         setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackId));
197     else
198         setCurrentAsyncCallChain(nullptr);
199 }
200 
didEnqueueEvent(EventTarget * eventTarget,Event * event,const ScriptValue & callFrames)201 void AsyncCallStackTracker::didEnqueueEvent(EventTarget* eventTarget, Event* event, const ScriptValue& callFrames)
202 {
203     ASSERT(eventTarget->executionContext());
204     ASSERT(isEnabled());
205     if (!validateCallFrames(callFrames))
206         return;
207     ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executionContext());
208     data->m_eventCallChains.set(event, createAsyncCallChain(event->type(), callFrames));
209 }
210 
didRemoveEvent(EventTarget * eventTarget,Event * event)211 void AsyncCallStackTracker::didRemoveEvent(EventTarget* eventTarget, Event* event)
212 {
213     ASSERT(eventTarget->executionContext());
214     ASSERT(isEnabled());
215     if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
216         data->m_eventCallChains.remove(event);
217 }
218 
willHandleEvent(EventTarget * eventTarget,Event * event,EventListener * listener,bool useCapture)219 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, Event* event, EventListener* listener, bool useCapture)
220 {
221     ASSERT(eventTarget->executionContext());
222     ASSERT(isEnabled());
223     if (XMLHttpRequest* xhr = toXmlHttpRequest(eventTarget)) {
224         willHandleXHREvent(xhr, eventTarget, event);
225     } else {
226         if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
227             setCurrentAsyncCallChain(data->m_eventCallChains.get(event));
228         else
229             setCurrentAsyncCallChain(nullptr);
230     }
231 }
232 
willLoadXHR(XMLHttpRequest * xhr,const ScriptValue & callFrames)233 void AsyncCallStackTracker::willLoadXHR(XMLHttpRequest* xhr, const ScriptValue& callFrames)
234 {
235     ASSERT(xhr->executionContext());
236     ASSERT(isEnabled());
237     if (!validateCallFrames(callFrames))
238         return;
239     ExecutionContextData* data = createContextDataIfNeeded(xhr->executionContext());
240     data->m_xhrCallChains.set(xhr, createAsyncCallChain(xhrSendName, callFrames));
241 }
242 
willHandleXHREvent(XMLHttpRequest * xhr,EventTarget * eventTarget,Event * event)243 void AsyncCallStackTracker::willHandleXHREvent(XMLHttpRequest* xhr, EventTarget* eventTarget, Event* event)
244 {
245     ASSERT(xhr->executionContext());
246     ASSERT(isEnabled());
247     if (ExecutionContextData* data = m_executionContextDataMap.get(xhr->executionContext())) {
248         bool isXHRDownload = (xhr == eventTarget);
249         if (isXHRDownload && event->type() == EventTypeNames::loadend)
250             setCurrentAsyncCallChain(data->m_xhrCallChains.take(xhr));
251         else
252             setCurrentAsyncCallChain(data->m_xhrCallChains.get(xhr));
253     } else {
254         setCurrentAsyncCallChain(nullptr);
255     }
256 }
257 
didEnqueueMutationRecord(ExecutionContext * context,MutationObserver * observer,const ScriptValue & callFrames)258 void AsyncCallStackTracker::didEnqueueMutationRecord(ExecutionContext* context, MutationObserver* observer, const ScriptValue& callFrames)
259 {
260     ASSERT(context);
261     ASSERT(isEnabled());
262     if (!validateCallFrames(callFrames))
263         return;
264     ExecutionContextData* data = createContextDataIfNeeded(context);
265     data->m_mutationObserverCallChains.set(observer, createAsyncCallChain(enqueueMutationRecordName, callFrames));
266 }
267 
hasEnqueuedMutationRecord(ExecutionContext * context,MutationObserver * observer)268 bool AsyncCallStackTracker::hasEnqueuedMutationRecord(ExecutionContext* context, MutationObserver* observer)
269 {
270     ASSERT(context);
271     ASSERT(isEnabled());
272     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
273         return data->m_mutationObserverCallChains.contains(observer);
274     return false;
275 }
276 
didClearAllMutationRecords(ExecutionContext * context,MutationObserver * observer)277 void AsyncCallStackTracker::didClearAllMutationRecords(ExecutionContext* context, MutationObserver* observer)
278 {
279     ASSERT(context);
280     ASSERT(isEnabled());
281     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
282         data->m_mutationObserverCallChains.remove(observer);
283 }
284 
willDeliverMutationRecords(ExecutionContext * context,MutationObserver * observer)285 void AsyncCallStackTracker::willDeliverMutationRecords(ExecutionContext* context, MutationObserver* observer)
286 {
287     ASSERT(context);
288     ASSERT(isEnabled());
289     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
290         setCurrentAsyncCallChain(data->m_mutationObserverCallChains.take(observer));
291     else
292         setCurrentAsyncCallChain(nullptr);
293 }
294 
didFireAsyncCall()295 void AsyncCallStackTracker::didFireAsyncCall()
296 {
297     clearCurrentAsyncCallChain();
298 }
299 
createAsyncCallChain(const String & description,const ScriptValue & callFrames)300 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createAsyncCallChain(const String& description, const ScriptValue& callFrames)
301 {
302     RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncCallStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTracker::AsyncCallChain());
303     ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1);
304     chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallStack(description, callFrames)));
305     return chain.release();
306 }
307 
setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain> chain)308 void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain> chain)
309 {
310     if (V8RecursionScope::recursionLevel(v8::Isolate::GetCurrent())) {
311         if (m_currentAsyncCallChain)
312             ++m_nestedAsyncCallCount;
313     } else {
314         // Current AsyncCallChain corresponds to the bottommost JS call frame.
315         m_currentAsyncCallChain = chain;
316         m_nestedAsyncCallCount = m_currentAsyncCallChain ? 1 : 0;
317     }
318 }
319 
clearCurrentAsyncCallChain()320 void AsyncCallStackTracker::clearCurrentAsyncCallChain()
321 {
322     if (!m_nestedAsyncCallCount)
323         return;
324     --m_nestedAsyncCallCount;
325     if (!m_nestedAsyncCallCount)
326         m_currentAsyncCallChain.clear();
327 }
328 
ensureMaxAsyncCallChainDepth(AsyncCallChain * chain,unsigned maxDepth)329 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth)
330 {
331     while (chain->m_callStacks.size() > maxDepth)
332         chain->m_callStacks.removeLast();
333 }
334 
validateCallFrames(const ScriptValue & callFrames)335 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames)
336 {
337     return !callFrames.isEmpty();
338 }
339 
createContextDataIfNeeded(ExecutionContext * context)340 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContextDataIfNeeded(ExecutionContext* context)
341 {
342     ExecutionContextData* data = m_executionContextDataMap.get(context);
343     if (!data) {
344         data = new AsyncCallStackTracker::ExecutionContextData(this, context);
345         m_executionContextDataMap.set(context, data);
346     }
347     return data;
348 }
349 
clear()350 void AsyncCallStackTracker::clear()
351 {
352     m_currentAsyncCallChain.clear();
353     m_nestedAsyncCallCount = 0;
354     ExecutionContextDataMap copy;
355     m_executionContextDataMap.swap(copy);
356     for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.end(); ++it)
357         delete it->value;
358 }
359 
360 } // namespace WebCore
361