Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(764)

Side by Side Diff: Source/core/inspector/AsyncCallStackTracker.cpp

Issue 118293002: DevTools: Support XHR async call stacks in the debugger. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: addressed Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2013 Google Inc. All rights reserved. 2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 17 matching lines...) Expand all
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */ 29 */
30 30
31 #include "config.h" 31 #include "config.h"
32 #include "core/inspector/AsyncCallStackTracker.h" 32 #include "core/inspector/AsyncCallStackTracker.h"
33 33
34 #include "core/dom/ContextLifecycleObserver.h" 34 #include "core/dom/ContextLifecycleObserver.h"
35 #include "core/dom/ExecutionContext.h" 35 #include "core/dom/ExecutionContext.h"
36 #include "core/events/EventTarget.h" 36 #include "core/events/EventTarget.h"
37 #include "core/events/RegisteredEventListener.h" 37 #include "core/events/RegisteredEventListener.h"
38 #include "core/xml/XMLHttpRequest.h"
39 #include "core/xml/XMLHttpRequestUpload.h"
40 #include "wtf/text/AtomicStringHash.h"
38 #include "wtf/text/StringBuilder.h" 41 #include "wtf/text/StringBuilder.h"
39 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 xhrSendName[] = "XMLHttpRequest.send";
49
50 }
51
40 namespace WebCore { 52 namespace WebCore {
41 53
42 class AsyncCallStackTracker::ExecutionContextData : public ContextLifecycleObser ver { 54 class AsyncCallStackTracker::ExecutionContextData : public ContextLifecycleObser ver {
43 WTF_MAKE_FAST_ALLOCATED; 55 WTF_MAKE_FAST_ALLOCATED;
44 public: 56 public:
45 typedef std::pair<RegisteredEventListener, RefPtr<AsyncCallChain> > EventLis tenerAsyncCallChain; 57 typedef std::pair<RegisteredEventListener, RefPtr<AsyncCallChain> > EventLis tenerAsyncCallChain;
46 typedef Vector<EventListenerAsyncCallChain, 1> EventListenerAsyncCallChainVe ctor; 58 typedef Vector<EventListenerAsyncCallChain, 1> EventListenerAsyncCallChainVe ctor;
47 typedef HashMap<AtomicString, EventListenerAsyncCallChainVector> EventListen erAsyncCallChainVectorHashMap; 59 typedef HashMap<AtomicString, EventListenerAsyncCallChainVector> EventListen erAsyncCallChainVectorHashMap;
48 60
49 ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* execu tionContext) 61 ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* execu tionContext)
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 } 116 }
105 return result.release(); 117 return result.release();
106 } 118 }
107 119
108 public: 120 public:
109 AsyncCallStackTracker* m_tracker; 121 AsyncCallStackTracker* m_tracker;
110 HashSet<int> m_intervalTimerIds; 122 HashSet<int> m_intervalTimerIds;
111 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains; 123 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains;
112 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains; 124 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains;
113 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap> m_eventTarge tCallChains; 125 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap> m_eventTarge tCallChains;
126 HashMap<EventTarget*, RefPtr<AsyncCallChain> > m_xhrCallChains;
114 }; 127 };
115 128
129 static XMLHttpRequest* toXmlHttpRequest(EventTarget* eventTarget)
130 {
131 const AtomicString& interfaceName = eventTarget->interfaceName();
132 if (interfaceName == EventTargetNames::XMLHttpRequest)
133 return static_cast<XMLHttpRequest*>(eventTarget);
134 if (interfaceName == EventTargetNames::XMLHttpRequestUpload)
135 return static_cast<XMLHttpRequestUpload*>(eventTarget)->xmlHttpRequest() ;
136 return 0;
137 }
138
116 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames) 139 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames)
117 : m_description(description) 140 : m_description(description)
118 , m_callFrames(callFrames) 141 , m_callFrames(callFrames)
119 { 142 {
120 } 143 }
121 144
122 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack() 145 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack()
123 { 146 {
124 } 147 }
125 148
(...skipping 14 matching lines...) Expand all
140 163
141 const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsync CallChain() const 164 const AsyncCallStackTracker::AsyncCallChain* AsyncCallStackTracker::currentAsync CallChain() const
142 { 165 {
143 if (m_currentAsyncCallChain) 166 if (m_currentAsyncCallChain)
144 ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCa llStackDepth); 167 ensureMaxAsyncCallChainDepth(m_currentAsyncCallChain.get(), m_maxAsyncCa llStackDepth);
145 return m_currentAsyncCallChain.get(); 168 return m_currentAsyncCallChain.get();
146 } 169 }
147 170
148 void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timer Id, bool singleShot, const ScriptValue& callFrames) 171 void AsyncCallStackTracker::didInstallTimer(ExecutionContext* context, int timer Id, bool singleShot, const ScriptValue& callFrames)
149 { 172 {
150 DEFINE_STATIC_LOCAL(String, setTimeoutName, ("setTimeout"));
151 DEFINE_STATIC_LOCAL(String, setIntervalName, ("setInterval"));
152
153 ASSERT(context); 173 ASSERT(context);
154 ASSERT(isEnabled()); 174 ASSERT(isEnabled());
155 if (!validateCallFrames(callFrames)) 175 if (!validateCallFrames(callFrames))
156 return; 176 return;
157 ASSERT(timerId > 0); 177 ASSERT(timerId > 0);
158 ExecutionContextData* data = createContextDataIfNeeded(context); 178 ExecutionContextData* data = createContextDataIfNeeded(context);
159 data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTi meoutName : setIntervalName, callFrames)); 179 data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTi meoutName : setIntervalName, callFrames));
160 if (!singleShot) 180 if (!singleShot)
161 data->m_intervalTimerIds.add(timerId); 181 data->m_intervalTimerIds.add(timerId);
162 } 182 }
(...skipping 10 matching lines...) Expand all
173 data->m_intervalTimerIds.remove(timerId); 193 data->m_intervalTimerIds.remove(timerId);
174 data->m_timerCallChains.remove(timerId); 194 data->m_timerCallChains.remove(timerId);
175 } 195 }
176 196
177 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId ) 197 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId )
178 { 198 {
179 ASSERT(context); 199 ASSERT(context);
180 ASSERT(isEnabled()); 200 ASSERT(isEnabled());
181 ASSERT(timerId > 0); 201 ASSERT(timerId > 0);
182 ASSERT(!m_currentAsyncCallChain); 202 ASSERT(!m_currentAsyncCallChain);
183 ExecutionContextData* data = m_executionContextDataMap.get(context); 203 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) {
184 if (!data) 204 if (data->m_intervalTimerIds.contains(timerId))
185 return; 205 setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId));
186 if (data->m_intervalTimerIds.contains(timerId)) 206 else
187 setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId)); 207 setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId));
188 else 208 } else {
189 setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId)); 209 setCurrentAsyncCallChain(0);
210 }
190 } 211 }
191 212
192 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames) 213 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames)
193 { 214 {
194 DEFINE_STATIC_LOCAL(String, requestAnimationFrameName, ("requestAnimationFra me"));
195
196 ASSERT(context); 215 ASSERT(context);
197 ASSERT(isEnabled()); 216 ASSERT(isEnabled());
198 if (!validateCallFrames(callFrames)) 217 if (!validateCallFrames(callFrames))
199 return; 218 return;
200 ASSERT(callbackId > 0); 219 ASSERT(callbackId > 0);
201 ExecutionContextData* data = createContextDataIfNeeded(context); 220 ExecutionContextData* data = createContextDataIfNeeded(context);
202 data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(reques tAnimationFrameName, callFrames)); 221 data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(reques tAnimationFrameName, callFrames));
203 } 222 }
204 223
205 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, i nt callbackId) 224 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, i nt callbackId)
206 { 225 {
207 ASSERT(context); 226 ASSERT(context);
208 ASSERT(isEnabled()); 227 ASSERT(isEnabled());
209 if (callbackId <= 0) 228 if (callbackId <= 0)
210 return; 229 return;
211 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) 230 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
212 data->m_animationFrameCallChains.remove(callbackId); 231 data->m_animationFrameCallChains.remove(callbackId);
213 } 232 }
214 233
215 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in t callbackId) 234 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in t callbackId)
216 { 235 {
217 ASSERT(context); 236 ASSERT(context);
218 ASSERT(isEnabled()); 237 ASSERT(isEnabled());
219 ASSERT(callbackId > 0); 238 ASSERT(callbackId > 0);
220 ASSERT(!m_currentAsyncCallChain); 239 ASSERT(!m_currentAsyncCallChain);
221 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) 240 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
222 setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackI d)); 241 setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackI d));
242 else
243 setCurrentAsyncCallChain(0);
223 } 244 }
224 245
225 void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptV alue& callFrames) 246 void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptV alue& callFrames)
226 { 247 {
227 ASSERT(eventTarget->executionContext()); 248 ASSERT(eventTarget->executionContext());
228 ASSERT(isEnabled()); 249 ASSERT(isEnabled());
229 if (!validateCallFrames(callFrames)) 250 if (!validateCallFrames(callFrames) || toXmlHttpRequest(eventTarget))
230 return; 251 return;
231 252
232 StringBuilder description; 253 StringBuilder description;
233 description.append(eventTarget->interfaceName()); 254 description.append(eventTarget->interfaceName());
234 if (!description.isEmpty()) 255 if (!description.isEmpty())
235 description.append("."); 256 description.append(".");
236 if (listener->isAttribute()) { 257 if (listener->isAttribute()) {
237 description.append("on"); 258 description.append("on");
238 description.append(eventType); 259 description.append(eventType);
239 } else { 260 } else {
(...skipping 19 matching lines...) Expand all
259 ASSERT(eventTarget->executionContext()); 280 ASSERT(eventTarget->executionContext());
260 ASSERT(isEnabled()); 281 ASSERT(isEnabled());
261 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext())) 282 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext()))
262 data->m_eventTargetCallChains.remove(eventTarget); 283 data->m_eventTargetCallChains.remove(eventTarget);
263 } 284 }
264 285
265 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const Atom icString& eventType, EventListener* listener, bool useCapture) 286 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const Atom icString& eventType, EventListener* listener, bool useCapture)
266 { 287 {
267 ASSERT(eventTarget->executionContext()); 288 ASSERT(eventTarget->executionContext());
268 ASSERT(isEnabled()); 289 ASSERT(isEnabled());
290 if (XMLHttpRequest* xhr = toXmlHttpRequest(eventTarget)) {
291 willHandleXHREvent(xhr, eventTarget, eventType);
292 return;
293 }
269 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext())) 294 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext()))
270 setCurrentAsyncCallChain(data->findEventListenerData(eventTarget, eventT ype, RegisteredEventListener(listener, useCapture))); 295 setCurrentAsyncCallChain(data->findEventListenerData(eventTarget, eventT ype, RegisteredEventListener(listener, useCapture)));
296 else
297 setCurrentAsyncCallChain(0);
298 }
299
300 void AsyncCallStackTracker::willLoadXHR(XMLHttpRequest* xhr, const ScriptValue& callFrames)
301 {
302 ASSERT(xhr->executionContext());
303 ASSERT(isEnabled());
304 if (!validateCallFrames(callFrames))
305 return;
306 ExecutionContextData* data = createContextDataIfNeeded(xhr->executionContext ());
307 data->m_xhrCallChains.set(xhr, createAsyncCallChain(xhrSendName, callFrames) );
308 }
309
310 void AsyncCallStackTracker::willHandleXHREvent(XMLHttpRequest* xhr, EventTarget* eventTarget, const AtomicString& eventType)
311 {
312 ASSERT(xhr->executionContext());
313 ASSERT(isEnabled());
314 if (ExecutionContextData* data = m_executionContextDataMap.get(xhr->executio nContext())) {
315 bool isXHRDownload = (xhr == eventTarget);
316 if (isXHRDownload && eventType == EventTypeNames::loadend)
317 setCurrentAsyncCallChain(data->m_xhrCallChains.take(xhr));
318 else
319 setCurrentAsyncCallChain(data->m_xhrCallChains.get(xhr));
320 } else {
321 setCurrentAsyncCallChain(0);
322 }
271 } 323 }
272 324
273 void AsyncCallStackTracker::didFireAsyncCall() 325 void AsyncCallStackTracker::didFireAsyncCall()
274 { 326 {
275 setCurrentAsyncCallChain(0); 327 clearCurrentAsyncCallChain();
276 } 328 }
277 329
278 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA syncCallChain(const String& description, const ScriptValue& callFrames) 330 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA syncCallChain(const String& description, const ScriptValue& callFrames)
279 { 331 {
280 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncC allStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTr acker::AsyncCallChain()); 332 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncC allStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTr acker::AsyncCallChain());
281 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1); 333 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1);
282 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta ck(description, callFrames))); 334 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta ck(description, callFrames)));
283 return chain.release(); 335 return chain.release();
284 } 336 }
285 337
286 void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain> chain) 338 void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain> chain)
287 { 339 {
288 if (m_currentAsyncCallChain) { 340 if (m_currentAsyncCallChain) {
289 m_nestedAsyncCallCount += chain ? 1 : -1; 341 ++m_nestedAsyncCallCount;
290 if (!m_nestedAsyncCallCount)
291 m_currentAsyncCallChain = 0;
292 } else if (chain) { 342 } else if (chain) {
293 m_currentAsyncCallChain = chain; 343 m_currentAsyncCallChain = chain;
294 m_nestedAsyncCallCount = 1; 344 m_nestedAsyncCallCount = 1;
295 } 345 }
296 } 346 }
297 347
348 void AsyncCallStackTracker::clearCurrentAsyncCallChain()
349 {
350 if (!m_nestedAsyncCallCount)
351 return;
352 --m_nestedAsyncCallCount;
353 if (!m_nestedAsyncCallCount)
354 m_currentAsyncCallChain.clear();
355 }
356
298 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth) 357 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth)
299 { 358 {
300 while (chain->m_callStacks.size() > maxDepth) 359 while (chain->m_callStacks.size() > maxDepth)
301 chain->m_callStacks.removeLast(); 360 chain->m_callStacks.removeLast();
302 } 361 }
303 362
304 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames) 363 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames)
305 { 364 {
306 return !callFrames.hasNoValue(); 365 return !callFrames.hasNoValue();
307 } 366 }
308 367
309 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex tDataIfNeeded(ExecutionContext* context) 368 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex tDataIfNeeded(ExecutionContext* context)
310 { 369 {
311 ExecutionContextData* data = m_executionContextDataMap.get(context); 370 ExecutionContextData* data = m_executionContextDataMap.get(context);
312 if (!data) { 371 if (!data) {
313 data = new AsyncCallStackTracker::ExecutionContextData(this, context); 372 data = new AsyncCallStackTracker::ExecutionContextData(this, context);
314 m_executionContextDataMap.set(context, data); 373 m_executionContextDataMap.set(context, data);
315 } 374 }
316 return data; 375 return data;
317 } 376 }
318 377
319 void AsyncCallStackTracker::clear() 378 void AsyncCallStackTracker::clear()
320 { 379 {
321 m_currentAsyncCallChain = 0; 380 m_currentAsyncCallChain.clear();
322 m_nestedAsyncCallCount = 0; 381 m_nestedAsyncCallCount = 0;
323 ExecutionContextDataMap copy; 382 ExecutionContextDataMap copy;
324 m_executionContextDataMap.swap(copy); 383 m_executionContextDataMap.swap(copy);
325 for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.e nd(); ++it) 384 for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.e nd(); ++it)
326 delete it->value; 385 delete it->value;
327 } 386 }
328 387
329 } // namespace WebCore 388 } // namespace WebCore
OLDNEW
« no previous file with comments | « Source/core/inspector/AsyncCallStackTracker.h ('k') | Source/core/inspector/InspectorDebuggerAgent.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698