2 * Copyright (C) 2013 Google 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 are
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
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.
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.
32 #include "core/inspector/AsyncCallStackTracker.h"
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/events/Event.h"
39 #include "core/events/EventTarget.h"
40 #include "core/xml/XMLHttpRequest.h"
41 #include "core/xml/XMLHttpRequestUpload.h"
42 #include "wtf/text/StringBuilder.h"
43 #include "wtf/text/StringHash.h"
48 static const char setTimeoutName[] = "setTimeout";
49 static const char setIntervalName[] = "setInterval";
50 static const char requestAnimationFrameName[] = "requestAnimationFrame";
51 static const char xhrSendName[] = "XMLHttpRequest.send";
52 static const char enqueueMutationRecordName[] = "Mutation";
58 void AsyncCallStackTracker::ExecutionContextData::contextDestroyed()
60 ASSERT(executionContext());
61 OwnPtrWillBeRawPtr<ExecutionContextData> self = m_tracker->m_executionContextDataMap.take(executionContext());
62 ASSERT_UNUSED(self, self == this);
63 ContextLifecycleObserver::contextDestroyed();
66 int AsyncCallStackTracker::ExecutionContextData::circularSequentialID()
68 ++m_circularSequentialID;
69 if (m_circularSequentialID <= 0)
70 m_circularSequentialID = 1;
71 return m_circularSequentialID;
74 void AsyncCallStackTracker::ExecutionContextData::trace(Visitor* visitor)
76 visitor->trace(m_tracker);
78 visitor->trace(m_timerCallChains);
79 visitor->trace(m_animationFrameCallChains);
80 visitor->trace(m_eventCallChains);
81 visitor->trace(m_xhrCallChains);
82 visitor->trace(m_mutationObserverCallChains);
83 visitor->trace(m_executionContextTaskCallChains);
84 visitor->trace(m_v8AsyncTaskCallChains);
85 visitor->trace(m_asyncOperationCallChains);
89 static XMLHttpRequest* toXmlHttpRequest(EventTarget* eventTarget)
91 const AtomicString& interfaceName = eventTarget->interfaceName();
92 if (interfaceName == EventTargetNames::XMLHttpRequest)
93 return static_cast<XMLHttpRequest*>(eventTarget);
94 if (interfaceName == EventTargetNames::XMLHttpRequestUpload)
95 return static_cast<XMLHttpRequestUpload*>(eventTarget)->xmlHttpRequest();
99 void AsyncCallStackTracker::AsyncCallChain::trace(Visitor* visitor)
101 visitor->trace(m_callStacks);
104 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames)
105 : m_description(description)
106 , m_callFrames(callFrames)
110 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack()
114 AsyncCallStackTracker::AsyncCallStackTracker()
115 : m_maxAsyncCallStackDepth(0)
116 , m_nestedAsyncCallCount(0)
120 void AsyncCallStackTracker::setAsyncCallStackDepth(int depth)
123 m_maxAsyncCallStackDepth = 0;
126 m_maxAsyncCallStackDepth = depth;
130 const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsyncCallChain() const
132 if (m_currentAsyncCallChain)
133 ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCallStackDepth);
134 return m_currentAsyncCallChain.get();
137 void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timerId, bool singleShot, const ScriptValue& callFrames)
141 if (!validateCallFrames(callFrames))
144 ExecutionContextData* data = createContextDataIfNeeded(context);
145 data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTimeoutName : setIntervalName, callFrames));
147 data->m_intervalTimerIds.add(timerId);
150 void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerId)
156 ExecutionContextData* data = m_executionContextDataMap.get(context);
159 data->m_intervalTimerIds.remove(timerId);
160 data->m_timerCallChains.remove(timerId);
163 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId)
168 ASSERT(!m_currentAsyncCallChain);
169 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) {
170 if (data->m_intervalTimerIds.contains(timerId))
171 setCurrentAsyncCallChain(context, data->m_timerCallChains.get(timerId));
173 setCurrentAsyncCallChain(context, data->m_timerCallChains.take(timerId));
175 setCurrentAsyncCallChain(context, nullptr);
179 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames)
183 if (!validateCallFrames(callFrames))
185 ASSERT(callbackId > 0);
186 ExecutionContextData* data = createContextDataIfNeeded(context);
187 data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(requestAnimationFrameName, callFrames));
190 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, int callbackId)
196 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
197 data->m_animationFrameCallChains.remove(callbackId);
200 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, int callbackId)
204 ASSERT(callbackId > 0);
205 ASSERT(!m_currentAsyncCallChain);
206 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
207 setCurrentAsyncCallChain(context, data->m_animationFrameCallChains.take(callbackId));
209 setCurrentAsyncCallChain(context, nullptr);
212 void AsyncCallStackTracker::didEnqueueEvent(EventTarget* eventTarget, Event* event, const ScriptValue& callFrames)
214 ASSERT(eventTarget->executionContext());
216 if (!validateCallFrames(callFrames))
218 ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executionContext());
219 data->m_eventCallChains.set(event, createAsyncCallChain(event->type(), callFrames));
222 void AsyncCallStackTracker::didRemoveEvent(EventTarget* eventTarget, Event* event)
224 ASSERT(eventTarget->executionContext());
226 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->executionContext()))
227 data->m_eventCallChains.remove(event);
230 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, Event* event, EventListener* listener, bool useCapture)
232 ASSERT(eventTarget->executionContext());
234 if (XMLHttpRequest* xhr = toXmlHttpRequest(eventTarget)) {
235 willHandleXHREvent(xhr, event);
237 ExecutionContext* context = eventTarget->executionContext();
238 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
239 setCurrentAsyncCallChain(context, data->m_eventCallChains.get(event));
241 setCurrentAsyncCallChain(context, nullptr);
245 void AsyncCallStackTracker::willLoadXHR(XMLHttpRequest* xhr, const ScriptValue& callFrames)
247 ASSERT(xhr->executionContext());
249 if (!validateCallFrames(callFrames))
251 ExecutionContextData* data = createContextDataIfNeeded(xhr->executionContext());
252 data->m_xhrCallChains.set(xhr, createAsyncCallChain(xhrSendName, callFrames));
255 void AsyncCallStackTracker::didLoadXHR(XMLHttpRequest* xhr)
257 ASSERT(xhr->executionContext());
259 if (ExecutionContextData* data = m_executionContextDataMap.get(xhr->executionContext()))
260 data->m_xhrCallChains.remove(xhr);
263 void AsyncCallStackTracker::willHandleXHREvent(XMLHttpRequest* xhr, Event* event)
265 ExecutionContext* context = xhr->executionContext();
268 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
269 setCurrentAsyncCallChain(context, data->m_xhrCallChains.get(xhr));
271 setCurrentAsyncCallChain(context, nullptr);
274 void AsyncCallStackTracker::didEnqueueMutationRecord(ExecutionContext* context, MutationObserver* observer, const ScriptValue& callFrames)
278 if (!validateCallFrames(callFrames))
280 ExecutionContextData* data = createContextDataIfNeeded(context);
281 data->m_mutationObserverCallChains.set(observer, createAsyncCallChain(enqueueMutationRecordName, callFrames));
284 bool AsyncCallStackTracker::hasEnqueuedMutationRecord(ExecutionContext* context, MutationObserver* observer)
288 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
289 return data->m_mutationObserverCallChains.contains(observer);
293 void AsyncCallStackTracker::didClearAllMutationRecords(ExecutionContext* context, MutationObserver* observer)
297 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
298 data->m_mutationObserverCallChains.remove(observer);
301 void AsyncCallStackTracker::willDeliverMutationRecords(ExecutionContext* context, MutationObserver* observer)
305 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
306 setCurrentAsyncCallChain(context, data->m_mutationObserverCallChains.take(observer));
308 setCurrentAsyncCallChain(context, nullptr);
311 void AsyncCallStackTracker::didPostExecutionContextTask(ExecutionContext* context, ExecutionContextTask* task, const ScriptValue& callFrames)
315 if (!validateCallFrames(callFrames))
317 ExecutionContextData* data = createContextDataIfNeeded(context);
318 data->m_executionContextTaskCallChains.set(task, createAsyncCallChain(task->taskNameForInstrumentation(), callFrames));
321 void AsyncCallStackTracker::didKillAllExecutionContextTasks(ExecutionContext* context)
325 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
326 data->m_executionContextTaskCallChains.clear();
329 void AsyncCallStackTracker::willPerformExecutionContextTask(ExecutionContext* context, ExecutionContextTask* task)
333 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
334 setCurrentAsyncCallChain(context, data->m_executionContextTaskCallChains.take(task));
336 setCurrentAsyncCallChain(context, nullptr);
339 static String makeV8AsyncTaskUniqueId(const String& eventName, int id)
341 StringBuilder builder;
342 builder.append(eventName);
343 builder.appendNumber(id);
344 return builder.toString();
347 void AsyncCallStackTracker::didEnqueueV8AsyncTask(ExecutionContext* context, const String& eventName, int id, const ScriptValue& callFrames)
351 if (!validateCallFrames(callFrames))
353 ExecutionContextData* data = createContextDataIfNeeded(context);
354 data->m_v8AsyncTaskCallChains.set(makeV8AsyncTaskUniqueId(eventName, id), createAsyncCallChain(eventName, callFrames));
357 void AsyncCallStackTracker::willHandleV8AsyncTask(ExecutionContext* context, const String& eventName, int id)
361 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
362 setCurrentAsyncCallChain(context, data->m_v8AsyncTaskCallChains.take(makeV8AsyncTaskUniqueId(eventName, id)));
364 setCurrentAsyncCallChain(context, nullptr);
367 int AsyncCallStackTracker::traceAsyncOperationStarting(ExecutionContext* context, const String& operationName, const ScriptValue& callFrames)
371 if (!validateCallFrames(callFrames))
373 ExecutionContextData* data = createContextDataIfNeeded(context);
374 int id = data->circularSequentialID();
375 while (data->m_asyncOperationCallChains.contains(id))
376 id = data->circularSequentialID();
377 data->m_asyncOperationCallChains.set(id, createAsyncCallChain(operationName, callFrames));
381 void AsyncCallStackTracker::traceAsyncOperationCompleted(ExecutionContext* context, int operationId)
385 if (operationId <= 0)
387 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
388 data->m_asyncOperationCallChains.remove(operationId);
391 void AsyncCallStackTracker::traceAsyncCallbackStarting(ExecutionContext* context, int operationId)
395 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
396 setCurrentAsyncCallChain(context, operationId > 0 ? data->m_asyncOperationCallChains.get(operationId) : nullptr);
398 setCurrentAsyncCallChain(context, nullptr);
401 void AsyncCallStackTracker::didFireAsyncCall()
403 clearCurrentAsyncCallChain();
406 PassRefPtrWillBeRawPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createAsyncCallChain(const String& description, const ScriptValue& callFrames)
408 if (callFrames.isEmpty()) {
409 ASSERT(m_currentAsyncCallChain);
410 return m_currentAsyncCallChain; // Propogate async call stack chain.
412 RefPtrWillBeRawPtr<AsyncCallChain> chain = adoptRefWillBeNoop(m_currentAsyncCallChain ? new AsyncCallStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTracker::AsyncCallChain());
413 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1);
414 chain->m_callStacks.prepend(adoptRefWillBeNoop(new AsyncCallStackTracker::AsyncCallStack(description, callFrames)));
415 return chain.release();
418 void AsyncCallStackTracker::setCurrentAsyncCallChain(ExecutionContext* context, PassRefPtrWillBeRawPtr<AsyncCallChain> chain)
420 if (chain && !V8RecursionScope::recursionLevel(toIsolate(context))) {
421 // Current AsyncCallChain corresponds to the bottommost JS call frame.
422 m_currentAsyncCallChain = chain;
423 m_nestedAsyncCallCount = 1;
425 if (m_currentAsyncCallChain)
426 ++m_nestedAsyncCallCount;
430 void AsyncCallStackTracker::clearCurrentAsyncCallChain()
432 if (!m_nestedAsyncCallCount)
434 --m_nestedAsyncCallCount;
435 if (!m_nestedAsyncCallCount)
436 m_currentAsyncCallChain.clear();
439 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth)
441 while (chain->m_callStacks.size() > maxDepth)
442 chain->m_callStacks.removeLast();
445 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames)
447 return !callFrames.isEmpty() || m_currentAsyncCallChain;
450 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContextDataIfNeeded(ExecutionContext* context)
452 ExecutionContextData* data = m_executionContextDataMap.get(context);
454 data = m_executionContextDataMap.set(context, adoptPtrWillBeNoop(new AsyncCallStackTracker::ExecutionContextData(this, context)))
455 .storedValue->value.get();
460 void AsyncCallStackTracker::clear()
462 m_currentAsyncCallChain.clear();
463 m_nestedAsyncCallCount = 0;
464 m_executionContextDataMap.clear();
467 void AsyncCallStackTracker::trace(Visitor* visitor)
469 visitor->trace(m_currentAsyncCallChain);
471 visitor->trace(m_executionContextDataMap);