Chromium Code Reviews| 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) | |
|
yurys
2014/11/14 07:38:31
Why not delete these methods completely if the con
| |
| 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 |