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