• 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/core/v8/V8Binding.h"
35 #include "bindings/core/v8/V8RecursionScope.h"
36 #include "core/dom/ExecutionContext.h"
37 #include "core/dom/ExecutionContextTask.h"
38 #include "core/dom/Microtask.h"
39 #include "core/events/Event.h"
40 #include "core/events/EventTarget.h"
41 #include "core/xml/XMLHttpRequest.h"
42 #include "core/xml/XMLHttpRequestUpload.h"
43 #include "wtf/text/StringBuilder.h"
44 #include "wtf/text/StringHash.h"
45 #include <v8.h>
46 
47 namespace {
48 
49 static const char setTimeoutName[] = "setTimeout";
50 static const char setIntervalName[] = "setInterval";
51 static const char requestAnimationFrameName[] = "requestAnimationFrame";
52 static const char xhrSendName[] = "XMLHttpRequest.send";
53 static const char enqueueMutationRecordName[] = "Mutation";
54 
55 }
56 
57 namespace blink {
58 
contextDestroyed()59 void AsyncCallStackTracker::ExecutionContextData::contextDestroyed()
60 {
61     ASSERT(executionContext());
62     OwnPtrWillBeRawPtr<ExecutionContextData> self = m_tracker->m_executionContextDataMap.take(executionContext());
63     ASSERT_UNUSED(self, self == this);
64     ContextLifecycleObserver::contextDestroyed();
65 }
66 
circularSequentialID()67 int AsyncCallStackTracker::ExecutionContextData::circularSequentialID()
68 {
69     ++m_circularSequentialID;
70     if (m_circularSequentialID <= 0)
71         m_circularSequentialID = 1;
72     return m_circularSequentialID;
73 }
74 
trace(Visitor * visitor)75 void AsyncCallStackTracker::ExecutionContextData::trace(Visitor* visitor)
76 {
77     visitor->trace(m_tracker);
78 #if ENABLE(OILPAN)
79     visitor->trace(m_timerCallChains);
80     visitor->trace(m_animationFrameCallChains);
81     visitor->trace(m_eventCallChains);
82     visitor->trace(m_xhrCallChains);
83     visitor->trace(m_mutationObserverCallChains);
84     visitor->trace(m_executionContextTaskCallChains);
85     visitor->trace(m_v8AsyncTaskCallChains);
86     visitor->trace(m_asyncOperationCallChains);
87 #endif
88 }
89 
toXmlHttpRequest(EventTarget * eventTarget)90 static XMLHttpRequest* toXmlHttpRequest(EventTarget* eventTarget)
91 {
92     const AtomicString& interfaceName = eventTarget->interfaceName();
93     if (interfaceName == EventTargetNames::XMLHttpRequest)
94         return static_cast<XMLHttpRequest*>(eventTarget);
95     if (interfaceName == EventTargetNames::XMLHttpRequestUpload)
96         return static_cast<XMLHttpRequestUpload*>(eventTarget)->xmlHttpRequest();
97     return 0;
98 }
99 
trace(Visitor * visitor)100 void AsyncCallStackTracker::AsyncCallChain::trace(Visitor* visitor)
101 {
102     visitor->trace(m_callStacks);
103 }
104 
AsyncCallStack(const String & description,const ScriptValue & callFrames)105 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames)
106     : m_description(description)
107     , m_callFrames(callFrames)
108 {
109 }
110 
~AsyncCallStack()111 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack()
112 {
113 }
114 
AsyncCallStackTracker()115 AsyncCallStackTracker::AsyncCallStackTracker()
116     : m_maxAsyncCallStackDepth(0)
117     , m_nestedAsyncCallCount(0)
118 {
119 }
120 
setAsyncCallStackDepth(int depth)121 void AsyncCallStackTracker::setAsyncCallStackDepth(int depth)
122 {
123     if (depth <= 0) {
124         m_maxAsyncCallStackDepth = 0;
125         clear();
126     } else {
127         m_maxAsyncCallStackDepth = depth;
128     }
129 }
130 
currentAsyncCallChain() const131 const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsyncCallChain() const
132 {
133     if (m_currentAsyncCallChain)
134         ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCallStackDepth);
135     return m_currentAsyncCallChain.get();
136 }
137 
didInstallTimer(ExecutionContext * context,int timerId,bool singleShot,const ScriptValue & callFrames)138 void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timerId, bool singleShot, const ScriptValue& callFrames)
139 {
140     ASSERT(context);
141     ASSERT(isEnabled());
142     if (!validateCallFrames(callFrames))
143         return;
144     ASSERT(timerId > 0);
145     ExecutionContextData* data = createContextDataIfNeeded(context);
146     data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTimeoutName : setIntervalName, callFrames));
147     if (!singleShot)
148         data->m_intervalTimerIds.add(timerId);
149 }
150 
didRemoveTimer(ExecutionContext * context,int timerId)151 void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerId)
152 {
153     ASSERT(context);
154     ASSERT(isEnabled());
155     if (timerId <= 0)
156         return;
157     ExecutionContextData* data = m_executionContextDataMap.get(context);
158     if (!data)
159         return;
160     data->m_intervalTimerIds.remove(timerId);
161     data->m_timerCallChains.remove(timerId);
162 }
163 
willFireTimer(ExecutionContext * context,int timerId)164 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId)
165 {
166     ASSERT(context);
167     ASSERT(isEnabled());
168     ASSERT(timerId > 0);
169     ASSERT(!m_currentAsyncCallChain);
170     if (ExecutionContextData* data = m_executionContextDataMap.get(context)) {
171         if (data->m_intervalTimerIds.contains(timerId))
172             setCurrentAsyncCallChain(context, data->m_timerCallChains.get(timerId));
173         else
174             setCurrentAsyncCallChain(context, data->m_timerCallChains.take(timerId));
175     } else {
176         setCurrentAsyncCallChain(context, nullptr);
177     }
178 }
179 
didRequestAnimationFrame(ExecutionContext * context,int callbackId,const ScriptValue & callFrames)180 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames)
181 {
182     ASSERT(context);
183     ASSERT(isEnabled());
184     if (!validateCallFrames(callFrames))
185         return;
186     ASSERT(callbackId > 0);
187     ExecutionContextData* data = createContextDataIfNeeded(context);
188     data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(requestAnimationFrameName, callFrames));
189 }
190 
didCancelAnimationFrame(ExecutionContext * context,int callbackId)191 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, int callbackId)
192 {
193     ASSERT(context);
194     ASSERT(isEnabled());
195     if (callbackId <= 0)
196         return;
197     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
198         data->m_animationFrameCallChains.remove(callbackId);
199 }
200 
willFireAnimationFrame(ExecutionContext * context,int callbackId)201 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, int callbackId)
202 {
203     ASSERT(context);
204     ASSERT(isEnabled());
205     ASSERT(callbackId > 0);
206     ASSERT(!m_currentAsyncCallChain);
207     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
208         setCurrentAsyncCallChain(context, data->m_animationFrameCallChains.take(callbackId));
209     else
210         setCurrentAsyncCallChain(context, nullptr);
211 }
212 
didEnqueueEvent(EventTarget * eventTarget,Event * event,const ScriptValue & callFrames)213 void AsyncCallStackTracker::didEnqueueEvent(EventTarget* eventTarget, Event* event, const ScriptValue& callFrames)
214 {
215     ASSERT(eventTarget->executionContext());
216     ASSERT(isEnabled());
217     if (!validateCallFrames(callFrames))
218         return;
219     ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executionContext());
220     data->m_eventCallChains.set(event, createAsyncCallChain(event->type(), callFrames));
221 }
222 
didRemoveEvent(EventTarget * eventTarget,Event * event)223 void AsyncCallStackTracker::didRemoveEvent(EventTarget* eventTarget, Event* event)
224 {
225     ASSERT(eventTarget->executionContext());
226     ASSERT(isEnabled());
227     if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
228         data->m_eventCallChains.remove(event);
229 }
230 
willHandleEvent(EventTarget * eventTarget,Event * event,EventListener * listener,bool useCapture)231 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, Event* event, EventListener* listener, bool useCapture)
232 {
233     ASSERT(eventTarget->executionContext());
234     ASSERT(isEnabled());
235     if (XMLHttpRequest* xhr = toXmlHttpRequest(eventTarget)) {
236         willHandleXHREvent(xhr, event);
237     } else {
238         ExecutionContext* context = eventTarget->executionContext();
239         if (ExecutionContextData* data = m_executionContextDataMap.get(context))
240             setCurrentAsyncCallChain(context, data->m_eventCallChains.get(event));
241         else
242             setCurrentAsyncCallChain(context, nullptr);
243     }
244 }
245 
willLoadXHR(XMLHttpRequest * xhr,const ScriptValue & callFrames)246 void AsyncCallStackTracker::willLoadXHR(XMLHttpRequest* xhr, const ScriptValue& callFrames)
247 {
248     ASSERT(xhr->executionContext());
249     ASSERT(isEnabled());
250     if (!validateCallFrames(callFrames))
251         return;
252     ExecutionContextData* data = createContextDataIfNeeded(xhr->executionContext());
253     data->m_xhrCallChains.set(xhr, createAsyncCallChain(xhrSendName, callFrames));
254 }
255 
didLoadXHR(XMLHttpRequest * xhr)256 void AsyncCallStackTracker::didLoadXHR(XMLHttpRequest* xhr)
257 {
258     ASSERT(xhr->executionContext());
259     ASSERT(isEnabled());
260     if (ExecutionContextData* data = m_executionContextDataMap.get(xhr->executionContext()))
261         data->m_xhrCallChains.remove(xhr);
262 }
263 
willHandleXHREvent(XMLHttpRequest * xhr,Event * event)264 void AsyncCallStackTracker::willHandleXHREvent(XMLHttpRequest* xhr, Event* event)
265 {
266     ExecutionContext* context = xhr->executionContext();
267     ASSERT(context);
268     ASSERT(isEnabled());
269     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
270         setCurrentAsyncCallChain(context, data->m_xhrCallChains.get(xhr));
271     else
272         setCurrentAsyncCallChain(context, nullptr);
273 }
274 
didEnqueueMutationRecord(ExecutionContext * context,MutationObserver * observer,const ScriptValue & callFrames)275 void AsyncCallStackTracker::didEnqueueMutationRecord(ExecutionContext* context, MutationObserver* observer, const ScriptValue& callFrames)
276 {
277     ASSERT(context);
278     ASSERT(isEnabled());
279     if (!validateCallFrames(callFrames))
280         return;
281     ExecutionContextData* data = createContextDataIfNeeded(context);
282     data->m_mutationObserverCallChains.set(observer, createAsyncCallChain(enqueueMutationRecordName, callFrames));
283 }
284 
hasEnqueuedMutationRecord(ExecutionContext * context,MutationObserver * observer)285 bool AsyncCallStackTracker::hasEnqueuedMutationRecord(ExecutionContext* context, MutationObserver* observer)
286 {
287     ASSERT(context);
288     ASSERT(isEnabled());
289     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
290         return data->m_mutationObserverCallChains.contains(observer);
291     return false;
292 }
293 
didClearAllMutationRecords(ExecutionContext * context,MutationObserver * observer)294 void AsyncCallStackTracker::didClearAllMutationRecords(ExecutionContext* context, MutationObserver* observer)
295 {
296     ASSERT(context);
297     ASSERT(isEnabled());
298     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
299         data->m_mutationObserverCallChains.remove(observer);
300 }
301 
willDeliverMutationRecords(ExecutionContext * context,MutationObserver * observer)302 void AsyncCallStackTracker::willDeliverMutationRecords(ExecutionContext* context, MutationObserver* observer)
303 {
304     ASSERT(context);
305     ASSERT(isEnabled());
306     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
307         setCurrentAsyncCallChain(context, data->m_mutationObserverCallChains.take(observer));
308     else
309         setCurrentAsyncCallChain(context, nullptr);
310 }
311 
didPostExecutionContextTask(ExecutionContext * context,ExecutionContextTask * task,const ScriptValue & callFrames)312 void AsyncCallStackTracker::didPostExecutionContextTask(ExecutionContext* context, ExecutionContextTask* task, const ScriptValue& callFrames)
313 {
314     ASSERT(context);
315     ASSERT(isEnabled());
316     if (!validateCallFrames(callFrames))
317         return;
318     ExecutionContextData* data = createContextDataIfNeeded(context);
319     data->m_executionContextTaskCallChains.set(task, createAsyncCallChain(task->taskNameForInstrumentation(), callFrames));
320 }
321 
didKillAllExecutionContextTasks(ExecutionContext * context)322 void AsyncCallStackTracker::didKillAllExecutionContextTasks(ExecutionContext* context)
323 {
324     ASSERT(context);
325     ASSERT(isEnabled());
326     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
327         data->m_executionContextTaskCallChains.clear();
328 }
329 
willPerformExecutionContextTask(ExecutionContext * context,ExecutionContextTask * task)330 void AsyncCallStackTracker::willPerformExecutionContextTask(ExecutionContext* context, ExecutionContextTask* task)
331 {
332     ASSERT(context);
333     ASSERT(isEnabled());
334     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
335         setCurrentAsyncCallChain(context, data->m_executionContextTaskCallChains.take(task));
336     else
337         setCurrentAsyncCallChain(context, nullptr);
338 }
339 
makeV8AsyncTaskUniqueId(const String & eventName,int id)340 static String makeV8AsyncTaskUniqueId(const String& eventName, int id)
341 {
342     StringBuilder builder;
343     builder.append(eventName);
344     builder.appendNumber(id);
345     return builder.toString();
346 }
347 
didEnqueueV8AsyncTask(ExecutionContext * context,const String & eventName,int id,const ScriptValue & callFrames)348 void AsyncCallStackTracker::didEnqueueV8AsyncTask(ExecutionContext* context, const String& eventName, int id, const ScriptValue& callFrames)
349 {
350     ASSERT(context);
351     ASSERT(isEnabled());
352     if (!validateCallFrames(callFrames))
353         return;
354     ExecutionContextData* data = createContextDataIfNeeded(context);
355     data->m_v8AsyncTaskCallChains.set(makeV8AsyncTaskUniqueId(eventName, id), createAsyncCallChain(eventName, callFrames));
356 }
357 
willHandleV8AsyncTask(ExecutionContext * context,const String & eventName,int id)358 void AsyncCallStackTracker::willHandleV8AsyncTask(ExecutionContext* context, const String& eventName, int id)
359 {
360     ASSERT(context);
361     ASSERT(isEnabled());
362     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
363         setCurrentAsyncCallChain(context, data->m_v8AsyncTaskCallChains.take(makeV8AsyncTaskUniqueId(eventName, id)));
364     else
365         setCurrentAsyncCallChain(context, nullptr);
366 }
367 
traceAsyncOperationStarting(ExecutionContext * context,const String & operationName,const ScriptValue & callFrames)368 int AsyncCallStackTracker::traceAsyncOperationStarting(ExecutionContext* context, const String& operationName, const ScriptValue& callFrames)
369 {
370     ASSERT(context);
371     ASSERT(isEnabled());
372     if (!validateCallFrames(callFrames))
373         return 0;
374     ExecutionContextData* data = createContextDataIfNeeded(context);
375     int id = data->circularSequentialID();
376     while (data->m_asyncOperationCallChains.contains(id))
377         id = data->circularSequentialID();
378     data->m_asyncOperationCallChains.set(id, createAsyncCallChain(operationName, callFrames));
379     return id;
380 }
381 
traceAsyncOperationCompleted(ExecutionContext * context,int operationId)382 void AsyncCallStackTracker::traceAsyncOperationCompleted(ExecutionContext* context, int operationId)
383 {
384     ASSERT(context);
385     ASSERT(isEnabled());
386     if (operationId <= 0)
387         return;
388     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
389         data->m_asyncOperationCallChains.remove(operationId);
390 }
391 
traceAsyncCallbackStarting(ExecutionContext * context,int operationId)392 void AsyncCallStackTracker::traceAsyncCallbackStarting(ExecutionContext* context, int operationId)
393 {
394     ASSERT(context);
395     ASSERT(isEnabled());
396     if (ExecutionContextData* data = m_executionContextDataMap.get(context))
397         setCurrentAsyncCallChain(context, operationId > 0 ? data->m_asyncOperationCallChains.get(operationId) : nullptr);
398     else
399         setCurrentAsyncCallChain(context, nullptr);
400 }
401 
didFireAsyncCall()402 void AsyncCallStackTracker::didFireAsyncCall()
403 {
404     clearCurrentAsyncCallChain();
405 }
406 
createAsyncCallChain(const String & description,const ScriptValue & callFrames)407 PassRefPtrWillBeRawPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createAsyncCallChain(const String& description, const ScriptValue& callFrames)
408 {
409     if (callFrames.isEmpty()) {
410         ASSERT(m_currentAsyncCallChain);
411         return m_currentAsyncCallChain; // Propogate async call stack chain.
412     }
413     RefPtrWillBeRawPtr<AsyncCallChain> chain = adoptRefWillBeNoop(m_currentAsyncCallChain ? new AsyncCallStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTracker::AsyncCallChain());
414     ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1);
415     chain->m_callStacks.prepend(adoptRefWillBeNoop(new AsyncCallStackTracker::AsyncCallStack(description, callFrames)));
416     return chain.release();
417 }
418 
setCurrentAsyncCallChain(ExecutionContext * context,PassRefPtrWillBeRawPtr<AsyncCallChain> chain)419 void AsyncCallStackTracker::setCurrentAsyncCallChain(ExecutionContext* context, PassRefPtrWillBeRawPtr<AsyncCallChain> chain)
420 {
421     v8::Isolate* isolate = toIsolate(context);
422     int recursionLevel = V8RecursionScope::recursionLevel(isolate);
423     if (chain && (!recursionLevel || (recursionLevel == 1 && Microtask::performingCheckpoint(isolate)))) {
424         // Current AsyncCallChain corresponds to the bottommost JS call frame.
425         m_currentAsyncCallChain = chain;
426         m_nestedAsyncCallCount = 1;
427     } else {
428         if (m_currentAsyncCallChain)
429             ++m_nestedAsyncCallCount;
430     }
431 }
432 
clearCurrentAsyncCallChain()433 void AsyncCallStackTracker::clearCurrentAsyncCallChain()
434 {
435     if (!m_nestedAsyncCallCount)
436         return;
437     --m_nestedAsyncCallCount;
438     if (!m_nestedAsyncCallCount)
439         m_currentAsyncCallChain.clear();
440 }
441 
ensureMaxAsyncCallChainDepth(AsyncCallChain * chain,unsigned maxDepth)442 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth)
443 {
444     while (chain->m_callStacks.size() > maxDepth)
445         chain->m_callStacks.removeLast();
446 }
447 
validateCallFrames(const ScriptValue & callFrames)448 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames)
449 {
450     return !callFrames.isEmpty() || m_currentAsyncCallChain;
451 }
452 
createContextDataIfNeeded(ExecutionContext * context)453 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContextDataIfNeeded(ExecutionContext* context)
454 {
455     ExecutionContextData* data = m_executionContextDataMap.get(context);
456     if (!data) {
457         data = m_executionContextDataMap.set(context, adoptPtrWillBeNoop(new AsyncCallStackTracker::ExecutionContextData(this, context)))
458             .storedValue->value.get();
459     }
460     return data;
461 }
462 
clear()463 void AsyncCallStackTracker::clear()
464 {
465     m_currentAsyncCallChain.clear();
466     m_nestedAsyncCallCount = 0;
467     m_executionContextDataMap.clear();
468 }
469 
trace(Visitor * visitor)470 void AsyncCallStackTracker::trace(Visitor* visitor)
471 {
472     visitor->trace(m_currentAsyncCallChain);
473 #if ENABLE(OILPAN)
474     visitor->trace(m_executionContextDataMap);
475 #endif
476 }
477 
478 } // namespace blink
479