OLD | NEW |
(Empty) | |
| 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/events/Event.h" |
| 39 #include "core/events/EventTarget.h" |
| 40 #include "wtf/text/StringBuilder.h" |
| 41 #include "wtf/text/StringHash.h" |
| 42 #include <v8.h> |
| 43 |
| 44 namespace { |
| 45 |
| 46 static const char setTimeoutName[] = "setTimeout"; |
| 47 static const char setIntervalName[] = "setInterval"; |
| 48 static const char requestAnimationFrameName[] = "requestAnimationFrame"; |
| 49 static const char enqueueMutationRecordName[] = "Mutation"; |
| 50 |
| 51 } |
| 52 |
| 53 namespace blink { |
| 54 |
| 55 void AsyncCallStackTracker::ExecutionContextData::contextDestroyed() |
| 56 { |
| 57 ASSERT(executionContext()); |
| 58 OwnPtr<ExecutionContextData> self = m_tracker->m_executionContextDataMap.tak
e(executionContext()); |
| 59 ASSERT_UNUSED(self, self == this); |
| 60 ContextLifecycleObserver::contextDestroyed(); |
| 61 } |
| 62 |
| 63 int AsyncCallStackTracker::ExecutionContextData::circularSequentialID() |
| 64 { |
| 65 ++m_circularSequentialID; |
| 66 if (m_circularSequentialID <= 0) |
| 67 m_circularSequentialID = 1; |
| 68 return m_circularSequentialID; |
| 69 } |
| 70 |
| 71 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description,
const ScriptValue& callFrames) |
| 72 : m_description(description) |
| 73 , m_callFrames(callFrames) |
| 74 { |
| 75 } |
| 76 |
| 77 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack() |
| 78 { |
| 79 } |
| 80 |
| 81 AsyncCallStackTracker::AsyncCallStackTracker() |
| 82 : m_maxAsyncCallStackDepth(0) |
| 83 , m_nestedAsyncCallCount(0) |
| 84 { |
| 85 } |
| 86 |
| 87 void AsyncCallStackTracker::setAsyncCallStackDepth(int depth) |
| 88 { |
| 89 if (depth <= 0) { |
| 90 m_maxAsyncCallStackDepth = 0; |
| 91 clear(); |
| 92 } else { |
| 93 m_maxAsyncCallStackDepth = depth; |
| 94 } |
| 95 } |
| 96 |
| 97 const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsync
CallChain() const |
| 98 { |
| 99 if (m_currentAsyncCallChain) |
| 100 ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCa
llStackDepth); |
| 101 return m_currentAsyncCallChain.get(); |
| 102 } |
| 103 |
| 104 void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timer
Id, bool singleShot, const ScriptValue& callFrames) |
| 105 { |
| 106 ASSERT(context); |
| 107 ASSERT(isEnabled()); |
| 108 if (!validateCallFrames(callFrames)) |
| 109 return; |
| 110 ASSERT(timerId > 0); |
| 111 ExecutionContextData* data = createContextDataIfNeeded(context); |
| 112 data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTi
meoutName : setIntervalName, callFrames)); |
| 113 if (!singleShot) |
| 114 data->m_intervalTimerIds.add(timerId); |
| 115 } |
| 116 |
| 117 void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerI
d) |
| 118 { |
| 119 ASSERT(context); |
| 120 ASSERT(isEnabled()); |
| 121 if (timerId <= 0) |
| 122 return; |
| 123 ExecutionContextData* data = m_executionContextDataMap.get(context); |
| 124 if (!data) |
| 125 return; |
| 126 data->m_intervalTimerIds.remove(timerId); |
| 127 data->m_timerCallChains.remove(timerId); |
| 128 } |
| 129 |
| 130 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId
) |
| 131 { |
| 132 ASSERT(context); |
| 133 ASSERT(isEnabled()); |
| 134 ASSERT(timerId > 0); |
| 135 ASSERT(!m_currentAsyncCallChain); |
| 136 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) { |
| 137 if (data->m_intervalTimerIds.contains(timerId)) |
| 138 setCurrentAsyncCallChain(context, data->m_timerCallChains.get(timerI
d)); |
| 139 else |
| 140 setCurrentAsyncCallChain(context, data->m_timerCallChains.take(timer
Id)); |
| 141 } else { |
| 142 setCurrentAsyncCallChain(context, nullptr); |
| 143 } |
| 144 } |
| 145 |
| 146 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context,
int callbackId, const ScriptValue& callFrames) |
| 147 { |
| 148 ASSERT(context); |
| 149 ASSERT(isEnabled()); |
| 150 if (!validateCallFrames(callFrames)) |
| 151 return; |
| 152 ASSERT(callbackId > 0); |
| 153 ExecutionContextData* data = createContextDataIfNeeded(context); |
| 154 data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(reques
tAnimationFrameName, callFrames)); |
| 155 } |
| 156 |
| 157 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, i
nt callbackId) |
| 158 { |
| 159 ASSERT(context); |
| 160 ASSERT(isEnabled()); |
| 161 if (callbackId <= 0) |
| 162 return; |
| 163 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 164 data->m_animationFrameCallChains.remove(callbackId); |
| 165 } |
| 166 |
| 167 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in
t callbackId) |
| 168 { |
| 169 ASSERT(context); |
| 170 ASSERT(isEnabled()); |
| 171 ASSERT(callbackId > 0); |
| 172 ASSERT(!m_currentAsyncCallChain); |
| 173 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 174 setCurrentAsyncCallChain(context, data->m_animationFrameCallChains.take(
callbackId)); |
| 175 else |
| 176 setCurrentAsyncCallChain(context, nullptr); |
| 177 } |
| 178 |
| 179 void AsyncCallStackTracker::didEnqueueEvent(EventTarget* eventTarget, Event* eve
nt, const ScriptValue& callFrames) |
| 180 { |
| 181 ASSERT(eventTarget->executionContext()); |
| 182 ASSERT(isEnabled()); |
| 183 if (!validateCallFrames(callFrames)) |
| 184 return; |
| 185 ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executio
nContext()); |
| 186 data->m_eventCallChains.set(event, createAsyncCallChain(event->type(), callF
rames)); |
| 187 } |
| 188 |
| 189 void AsyncCallStackTracker::didRemoveEvent(EventTarget* eventTarget, Event* even
t) |
| 190 { |
| 191 ASSERT(eventTarget->executionContext()); |
| 192 ASSERT(isEnabled()); |
| 193 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget->
executionContext())) |
| 194 data->m_eventCallChains.remove(event); |
| 195 } |
| 196 |
| 197 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, Event* eve
nt, EventListener* listener, bool useCapture) |
| 198 { |
| 199 ASSERT(eventTarget->executionContext()); |
| 200 ASSERT(isEnabled()); |
| 201 ExecutionContext* context = eventTarget->executionContext(); |
| 202 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 203 setCurrentAsyncCallChain(context, data->m_eventCallChains.get(event)); |
| 204 else |
| 205 setCurrentAsyncCallChain(context, nullptr); |
| 206 } |
| 207 |
| 208 void AsyncCallStackTracker::didEnqueueMutationRecord(ExecutionContext* context,
MutationObserver* observer, const ScriptValue& callFrames) |
| 209 { |
| 210 ASSERT(context); |
| 211 ASSERT(isEnabled()); |
| 212 if (!validateCallFrames(callFrames)) |
| 213 return; |
| 214 ExecutionContextData* data = createContextDataIfNeeded(context); |
| 215 data->m_mutationObserverCallChains.set(observer, createAsyncCallChain(enqueu
eMutationRecordName, callFrames)); |
| 216 } |
| 217 |
| 218 bool AsyncCallStackTracker::hasEnqueuedMutationRecord(ExecutionContext* context,
MutationObserver* observer) |
| 219 { |
| 220 ASSERT(context); |
| 221 ASSERT(isEnabled()); |
| 222 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 223 return data->m_mutationObserverCallChains.contains(observer); |
| 224 return false; |
| 225 } |
| 226 |
| 227 void AsyncCallStackTracker::didClearAllMutationRecords(ExecutionContext* context
, MutationObserver* observer) |
| 228 { |
| 229 ASSERT(context); |
| 230 ASSERT(isEnabled()); |
| 231 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 232 data->m_mutationObserverCallChains.remove(observer); |
| 233 } |
| 234 |
| 235 void AsyncCallStackTracker::willDeliverMutationRecords(ExecutionContext* context
, MutationObserver* observer) |
| 236 { |
| 237 ASSERT(context); |
| 238 ASSERT(isEnabled()); |
| 239 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 240 setCurrentAsyncCallChain(context, data->m_mutationObserverCallChains.tak
e(observer)); |
| 241 else |
| 242 setCurrentAsyncCallChain(context, nullptr); |
| 243 } |
| 244 |
| 245 // void AsyncCallStackTracker::didPostExecutionContextTask(ExecutionContext* con
text, ExecutionContextTask* task, const ScriptValue& callFrames) |
| 246 // { |
| 247 // ASSERT(context); |
| 248 // ASSERT(isEnabled()); |
| 249 // if (!validateCallFrames(callFrames)) |
| 250 // return; |
| 251 // ExecutionContextData* data = createContextDataIfNeeded(context); |
| 252 // data->m_executionContextTaskCallChains.set(task, createAsyncCallChain(tas
k->taskNameForInstrumentation(), callFrames)); |
| 253 // } |
| 254 |
| 255 // void AsyncCallStackTracker::didKillAllExecutionContextTasks(ExecutionContext*
context) |
| 256 // { |
| 257 // ASSERT(context); |
| 258 // ASSERT(isEnabled()); |
| 259 // if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 260 // data->m_executionContextTaskCallChains.clear(); |
| 261 // } |
| 262 |
| 263 // void AsyncCallStackTracker::willPerformExecutionContextTask(ExecutionContext*
context, ExecutionContextTask* task) |
| 264 // { |
| 265 // ASSERT(context); |
| 266 // ASSERT(isEnabled()); |
| 267 // if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 268 // setCurrentAsyncCallChain(context, data->m_executionContextTaskCallCha
ins.take(task)); |
| 269 // else |
| 270 // setCurrentAsyncCallChain(context, nullptr); |
| 271 // } |
| 272 |
| 273 static String makeV8AsyncTaskUniqueId(const String& eventName, int id) |
| 274 { |
| 275 StringBuilder builder; |
| 276 builder.append(eventName); |
| 277 builder.appendNumber(id); |
| 278 return builder.toString(); |
| 279 } |
| 280 |
| 281 void AsyncCallStackTracker::didEnqueueV8AsyncTask(ExecutionContext* context, con
st String& eventName, int id, const ScriptValue& callFrames) |
| 282 { |
| 283 ASSERT(context); |
| 284 ASSERT(isEnabled()); |
| 285 if (!validateCallFrames(callFrames)) |
| 286 return; |
| 287 ExecutionContextData* data = createContextDataIfNeeded(context); |
| 288 data->m_v8AsyncTaskCallChains.set(makeV8AsyncTaskUniqueId(eventName, id), cr
eateAsyncCallChain(eventName, callFrames)); |
| 289 } |
| 290 |
| 291 void AsyncCallStackTracker::willHandleV8AsyncTask(ExecutionContext* context, con
st String& eventName, int id) |
| 292 { |
| 293 ASSERT(context); |
| 294 ASSERT(isEnabled()); |
| 295 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 296 setCurrentAsyncCallChain(context, data->m_v8AsyncTaskCallChains.take(mak
eV8AsyncTaskUniqueId(eventName, id))); |
| 297 else |
| 298 setCurrentAsyncCallChain(context, nullptr); |
| 299 } |
| 300 |
| 301 int AsyncCallStackTracker::traceAsyncOperationStarting(ExecutionContext* context
, const String& operationName, const ScriptValue& callFrames) |
| 302 { |
| 303 ASSERT(context); |
| 304 ASSERT(isEnabled()); |
| 305 if (!validateCallFrames(callFrames)) |
| 306 return 0; |
| 307 ExecutionContextData* data = createContextDataIfNeeded(context); |
| 308 int id = data->circularSequentialID(); |
| 309 while (data->m_asyncOperationCallChains.contains(id)) |
| 310 id = data->circularSequentialID(); |
| 311 data->m_asyncOperationCallChains.set(id, createAsyncCallChain(operationName,
callFrames)); |
| 312 return id; |
| 313 } |
| 314 |
| 315 void AsyncCallStackTracker::traceAsyncOperationCompleted(ExecutionContext* conte
xt, int operationId) |
| 316 { |
| 317 ASSERT(context); |
| 318 ASSERT(isEnabled()); |
| 319 if (operationId <= 0) |
| 320 return; |
| 321 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 322 data->m_asyncOperationCallChains.remove(operationId); |
| 323 } |
| 324 |
| 325 void AsyncCallStackTracker::traceAsyncCallbackStarting(ExecutionContext* context
, int operationId) |
| 326 { |
| 327 ASSERT(context); |
| 328 ASSERT(isEnabled()); |
| 329 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) |
| 330 setCurrentAsyncCallChain(context, operationId > 0 ? data->m_asyncOperati
onCallChains.get(operationId) : nullptr); |
| 331 else |
| 332 setCurrentAsyncCallChain(context, nullptr); |
| 333 } |
| 334 |
| 335 void AsyncCallStackTracker::didFireAsyncCall() |
| 336 { |
| 337 clearCurrentAsyncCallChain(); |
| 338 } |
| 339 |
| 340 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA
syncCallChain(const String& description, const ScriptValue& callFrames) |
| 341 { |
| 342 if (callFrames.isEmpty()) { |
| 343 ASSERT(m_currentAsyncCallChain); |
| 344 return m_currentAsyncCallChain; // Propogate async call stack chain. |
| 345 } |
| 346 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncC
allStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTr
acker::AsyncCallChain()); |
| 347 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1); |
| 348 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta
ck(description, callFrames))); |
| 349 return chain.release(); |
| 350 } |
| 351 |
| 352 void AsyncCallStackTracker::setCurrentAsyncCallChain(ExecutionContext* context,
PassRefPtr<AsyncCallChain> chain) |
| 353 { |
| 354 if (chain && !V8RecursionScope::recursionLevel(toIsolate(context))) { |
| 355 // Current AsyncCallChain corresponds to the bottommost JS call frame. |
| 356 m_currentAsyncCallChain = chain; |
| 357 m_nestedAsyncCallCount = 1; |
| 358 } else { |
| 359 if (m_currentAsyncCallChain) |
| 360 ++m_nestedAsyncCallCount; |
| 361 } |
| 362 } |
| 363 |
| 364 void AsyncCallStackTracker::clearCurrentAsyncCallChain() |
| 365 { |
| 366 if (!m_nestedAsyncCallCount) |
| 367 return; |
| 368 --m_nestedAsyncCallCount; |
| 369 if (!m_nestedAsyncCallCount) |
| 370 m_currentAsyncCallChain.clear(); |
| 371 } |
| 372 |
| 373 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain,
unsigned maxDepth) |
| 374 { |
| 375 while (chain->m_callStacks.size() > maxDepth) |
| 376 chain->m_callStacks.removeLast(); |
| 377 } |
| 378 |
| 379 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames) |
| 380 { |
| 381 return !callFrames.isEmpty() || m_currentAsyncCallChain; |
| 382 } |
| 383 |
| 384 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex
tDataIfNeeded(ExecutionContext* context) |
| 385 { |
| 386 ExecutionContextData* data = m_executionContextDataMap.get(context); |
| 387 if (!data) { |
| 388 data = m_executionContextDataMap.set(context, adoptPtr(new AsyncCallStac
kTracker::ExecutionContextData(this, context))) |
| 389 .storedValue->value.get(); |
| 390 } |
| 391 return data; |
| 392 } |
| 393 |
| 394 void AsyncCallStackTracker::clear() |
| 395 { |
| 396 m_currentAsyncCallChain.clear(); |
| 397 m_nestedAsyncCallCount = 0; |
| 398 m_executionContextDataMap.clear(); |
| 399 } |
| 400 |
| 401 } // namespace blink |
OLD | NEW |