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 |