Home | History | Annotate | Download | only in inspector
      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