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 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 67 int AsyncCallStackTracker::ExecutionContextData::circularSequentialID() 68 { 69 ++m_circularSequentialID; 70 if (m_circularSequentialID <= 0) 71 m_circularSequentialID = 1; 72 return m_circularSequentialID; 73 } 74 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 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 100 void AsyncCallStackTracker::AsyncCallChain::trace(Visitor* visitor) 101 { 102 visitor->trace(m_callStacks); 103 } 104 105 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames) 106 : m_description(description) 107 , m_callFrames(callFrames) 108 { 109 } 110 111 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack() 112 { 113 } 114 115 AsyncCallStackTracker::AsyncCallStackTracker() 116 : m_maxAsyncCallStackDepth(0) 117 , m_nestedAsyncCallCount(0) 118 { 119 } 120 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 131 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 402 void AsyncCallStackTracker::didFireAsyncCall() 403 { 404 clearCurrentAsyncCallChain(); 405 } 406 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 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 433 void AsyncCallStackTracker::clearCurrentAsyncCallChain() 434 { 435 if (!m_nestedAsyncCallCount) 436 return; 437 --m_nestedAsyncCallCount; 438 if (!m_nestedAsyncCallCount) 439 m_currentAsyncCallChain.clear(); 440 } 441 442 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth) 443 { 444 while (chain->m_callStacks.size() > maxDepth) 445 chain->m_callStacks.removeLast(); 446 } 447 448 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames) 449 { 450 return !callFrames.isEmpty() || m_currentAsyncCallChain; 451 } 452 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 463 void AsyncCallStackTracker::clear() 464 { 465 m_currentAsyncCallChain.clear(); 466 m_nestedAsyncCallCount = 0; 467 m_executionContextDataMap.clear(); 468 } 469 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