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

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

Issue 114033002: DevTools: Capture async stacks for event listeners. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: deal with nested async calls 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 15 matching lines...) Expand all
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 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. 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"
37 #include "core/events/RegisteredEventListener.h"
38 #include "wtf/text/StringBuilder.h"
36 39
37 namespace WebCore { 40 namespace WebCore {
38 41
39 class AsyncCallStackTracker::ExecutionContextData : public ContextLifecycleObser ver { 42 class AsyncCallStackTracker::ExecutionContextData : public ContextLifecycleObser ver {
40 WTF_MAKE_FAST_ALLOCATED; 43 WTF_MAKE_FAST_ALLOCATED;
41 public: 44 public:
45 typedef std::pair<RegisteredEventListener, RefPtr<AsyncCallChain> > EventLis tenerAsyncCallChain;
46 typedef Vector<EventListenerAsyncCallChain, 1> EventListenerAsyncCallChainVe ctor;
47 typedef HashMap<AtomicString, EventListenerAsyncCallChainVector> EventListen erAsyncCallChainVectorHashMap;
48
42 ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* execu tionContext) 49 ExecutionContextData(AsyncCallStackTracker* tracker, ExecutionContext* execu tionContext)
43 : ContextLifecycleObserver(executionContext) 50 : ContextLifecycleObserver(executionContext)
44 , m_tracker(tracker) 51 , m_tracker(tracker)
45 { 52 {
46 } 53 }
47 54
48 virtual void contextDestroyed() OVERRIDE 55 virtual void contextDestroyed() OVERRIDE
49 { 56 {
50 m_tracker->contextDestroyed(executionContext()); 57 m_tracker->contextDestroyed(executionContext());
51 ContextLifecycleObserver::contextDestroyed(); 58 ContextLifecycleObserver::contextDestroyed();
52 } 59 }
53 60
61 void add(EventTarget* eventTarget, const AtomicString& eventType, const Even tListenerAsyncCallChain& item)
yurys 2013/12/17 13:01:59 addEventListenerData?
aandrey 2013/12/17 13:24:03 Done.
62 {
63 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap>::iterato r it = m_eventTargetCallChains.find(eventTarget);
64 if (it == m_eventTargetCallChains.end())
65 it = m_eventTargetCallChains.set(eventTarget, EventListenerAsyncCall ChainVectorHashMap()).iterator;
66 EventListenerAsyncCallChainVectorHashMap& map = it->value;
67 EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventT ype);
68 if (it2 == map.end())
69 it2 = map.set(eventType, EventListenerAsyncCallChainVector()).iterat or;
70 it2->value.append(item);
71 }
72
73 void remove(EventTarget* eventTarget, const AtomicString& eventType, const R egisteredEventListener& item)
74 {
75 get(eventTarget, eventType, item, true);
76 }
77
78 PassRefPtr<AsyncCallChain> get(EventTarget* eventTarget, const AtomicString& eventType, const RegisteredEventListener& item, bool remove = false)
79 {
80 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap>::iterato r it = m_eventTargetCallChains.find(eventTarget);
81 if (it == m_eventTargetCallChains.end())
82 return 0;
83 EventListenerAsyncCallChainVectorHashMap& map = it->value;
84 EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventT ype);
85 if (it2 == map.end())
86 return 0;
87 RefPtr<AsyncCallChain> result;
88 EventListenerAsyncCallChainVector& vector = it2->value;
89 for (size_t i = 0; i < vector.size(); ++i) {
90 if (vector[i].first == item) {
91 result = vector[i].second;
92 if (remove) {
93 vector.remove(i);
94 if (vector.isEmpty())
95 map.remove(it2);
96 if (map.isEmpty())
97 m_eventTargetCallChains.remove(it);
98 }
99 break;
100 }
101 }
102 return result.release();
103 }
104
54 private: 105 private:
55 friend class AsyncCallStackTracker; 106 friend class AsyncCallStackTracker;
yurys 2013/12/17 13:01:59 All these field should be public since ExecutionCo
aandrey 2013/12/17 13:24:03 Done.
56 AsyncCallStackTracker* m_tracker; 107 AsyncCallStackTracker* m_tracker;
57 HashSet<int> m_intervalTimerIds; 108 HashSet<int> m_intervalTimerIds;
58 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains; 109 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains;
59 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains; 110 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains;
111 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap> m_eventTarge tCallChains;
60 }; 112 };
61 113
62 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames) 114 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames)
63 : m_description(description) 115 : m_description(description)
64 , m_callFrames(callFrames) 116 , m_callFrames(callFrames)
65 { 117 {
66 } 118 }
67 119
68 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack() 120 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack()
69 { 121 {
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
118 data->m_intervalTimerIds.remove(timerId); 170 data->m_intervalTimerIds.remove(timerId);
119 data->m_timerCallChains.remove(timerId); 171 data->m_timerCallChains.remove(timerId);
120 } 172 }
121 173
122 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId ) 174 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId )
123 { 175 {
124 ASSERT(context); 176 ASSERT(context);
125 if (!isEnabled()) 177 if (!isEnabled())
126 return; 178 return;
127 ASSERT(timerId > 0); 179 ASSERT(timerId > 0);
128 ASSERT(!m_currentAsyncCallChain);
yurys 2013/12/17 13:01:59 Why did you remove this assert? Can it fail in a w
aandrey 2013/12/17 13:07:48 We can receive nested willHandleEvent() callbacks,
aandrey 2013/12/17 13:24:03 Done.
yurys 2013/12/18 09:22:15 I agree that it make sense for willHandleEvent whi
aandrey 2013/12/18 09:32:09 I think they can not. Added those ASSERT's back.
129 ExecutionContextData* data = m_executionContextDataMap.get(context); 180 ExecutionContextData* data = m_executionContextDataMap.get(context);
130 if (!data) 181 if (!data)
131 return; 182 return;
132 if (data->m_intervalTimerIds.contains(timerId)) 183 if (data->m_intervalTimerIds.contains(timerId))
133 m_currentAsyncCallChain = data->m_timerCallChains.get(timerId); 184 setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId));
134 else 185 else
135 m_currentAsyncCallChain = data->m_timerCallChains.take(timerId); 186 setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId));
136 } 187 }
137 188
138 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames) 189 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames)
139 { 190 {
140 DEFINE_STATIC_LOCAL(String, requestAnimationFrameName, ("requestAnimationFra me")); 191 DEFINE_STATIC_LOCAL(String, requestAnimationFrameName, ("requestAnimationFra me"));
141 192
142 ASSERT(context); 193 ASSERT(context);
143 ASSERT(isEnabled()); 194 ASSERT(isEnabled());
144 if (!validateCallFrames(callFrames)) 195 if (!validateCallFrames(callFrames))
145 return; 196 return;
(...skipping 10 matching lines...) Expand all
156 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) 207 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
157 data->m_animationFrameCallChains.remove(callbackId); 208 data->m_animationFrameCallChains.remove(callbackId);
158 } 209 }
159 210
160 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in t callbackId) 211 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in t callbackId)
161 { 212 {
162 ASSERT(context); 213 ASSERT(context);
163 if (!isEnabled()) 214 if (!isEnabled())
164 return; 215 return;
165 ASSERT(callbackId > 0); 216 ASSERT(callbackId > 0);
166 ASSERT(!m_currentAsyncCallChain);
167 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) 217 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
168 m_currentAsyncCallChain = data->m_animationFrameCallChains.take(callback Id); 218 setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackI d));
219 }
220
221 void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptV alue& callFrames)
222 {
223 ASSERT(eventTarget->executionContext());
224 ASSERT(isEnabled());
225 if (!validateCallFrames(callFrames))
226 return;
227
228 StringBuilder description;
229 description.append(eventTarget->interfaceName());
230 if (!description.isEmpty())
231 description.append(".");
232 if (listener->isAttribute()) {
233 description.append("on");
234 description.append(eventType);
235 } else {
236 description.append("addEventListener(\"");
237 description.append(eventType);
238 description.append("\")");
239 }
240
241 ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executio nContext());
242 data->add(eventTarget, eventType, std::make_pair(RegisteredEventListener(lis tener, useCapture), createAsyncCallChain(description.toString(), callFrames)));
243 }
244
245 void AsyncCallStackTracker::didRemoveEventListener(EventTarget* eventTarget, con st AtomicString& eventType, EventListener* listener, bool useCapture)
246 {
247 ASSERT(eventTarget->executionContext());
248 if (!isEnabled())
249 return;
250 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext()))
251 data->remove(eventTarget, eventType, RegisteredEventListener(listener, u seCapture));
252 }
253
254 void AsyncCallStackTracker::didRemoveAllEventListeners(EventTarget* eventTarget)
255 {
256 ASSERT(eventTarget->executionContext());
257 if (!isEnabled())
258 return;
259 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext()))
260 data->m_eventTargetCallChains.remove(eventTarget);
261 }
262
263 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const Atom icString& eventType, EventListener* listener, bool useCapture)
264 {
265 ASSERT(eventTarget->executionContext());
266 if (!isEnabled())
267 return;
268 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext()))
269 setCurrentAsyncCallChain(data->get(eventTarget, eventType, RegisteredEve ntListener(listener, useCapture)));
169 } 270 }
170 271
171 void AsyncCallStackTracker::didFireAsyncCall() 272 void AsyncCallStackTracker::didFireAsyncCall()
172 { 273 {
173 m_currentAsyncCallChain = 0; 274 setCurrentAsyncCallChain(0);
174 } 275 }
175 276
176 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA syncCallChain(const String& description, const ScriptValue& callFrames) 277 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA syncCallChain(const String& description, const ScriptValue& callFrames)
177 { 278 {
178 ASSERT(isEnabled()); 279 ASSERT(isEnabled());
179 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncC allStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTr acker::AsyncCallChain()); 280 RefPtr<AsyncCallChain> chain = adoptRef(m_currentAsyncCallChain ? new AsyncC allStackTracker::AsyncCallChain(*m_currentAsyncCallChain) : new AsyncCallStackTr acker::AsyncCallChain());
180 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1); 281 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1);
181 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta ck(description, callFrames))); 282 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta ck(description, callFrames)));
182 return chain.release(); 283 return chain.release();
183 } 284 }
184 285
286 void AsyncCallStackTracker::setCurrentAsyncCallChain(PassRefPtr<AsyncCallChain> chain)
287 {
288 if (m_currentAsyncCallChain) {
289 m_nestedAsyncCallCount += chain ? 1 : -1;
290 if (!m_nestedAsyncCallCount)
291 m_currentAsyncCallChain = 0;
292 } else if (chain) {
293 m_currentAsyncCallChain = chain;
294 m_nestedAsyncCallCount = 1;
295 }
296 }
297
185 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth) 298 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth)
186 { 299 {
187 while (chain->m_callStacks.size() > maxDepth) 300 while (chain->m_callStacks.size() > maxDepth)
188 chain->m_callStacks.removeLast(); 301 chain->m_callStacks.removeLast();
189 } 302 }
190 303
191 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames) 304 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames)
192 { 305 {
193 return !callFrames.hasNoValue(); 306 return !callFrames.hasNoValue();
194 } 307 }
(...skipping 11 matching lines...) Expand all
206 if (!data) { 319 if (!data) {
207 data = new AsyncCallStackTracker::ExecutionContextData(this, context); 320 data = new AsyncCallStackTracker::ExecutionContextData(this, context);
208 m_executionContextDataMap.set(context, data); 321 m_executionContextDataMap.set(context, data);
209 } 322 }
210 return data; 323 return data;
211 } 324 }
212 325
213 void AsyncCallStackTracker::clear() 326 void AsyncCallStackTracker::clear()
214 { 327 {
215 m_currentAsyncCallChain = 0; 328 m_currentAsyncCallChain = 0;
329 m_nestedAsyncCallCount = 0;
216 Vector<ExecutionContextData*> contextsData; 330 Vector<ExecutionContextData*> contextsData;
217 copyValuesToVector(m_executionContextDataMap, contextsData); 331 copyValuesToVector(m_executionContextDataMap, contextsData);
218 m_executionContextDataMap.clear(); 332 m_executionContextDataMap.clear();
219 for (Vector<ExecutionContextData*>::const_iterator it = contextsData.begin() ; it != contextsData.end(); ++it) 333 for (Vector<ExecutionContextData*>::const_iterator it = contextsData.begin() ; it != contextsData.end(); ++it)
220 delete *it; 334 delete *it;
221 } 335 }
222 336
223 } // namespace WebCore 337 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698