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