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

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: 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 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 ASSERT(executionContext()); 57 ASSERT(executionContext());
51 ExecutionContextData* self = m_tracker->m_executionContextDataMap.take(e xecutionContext()); 58 ExecutionContextData* self = m_tracker->m_executionContextDataMap.take(e xecutionContext());
52 ASSERT(self == this); 59 ASSERT(self == this);
53 ContextLifecycleObserver::contextDestroyed(); 60 ContextLifecycleObserver::contextDestroyed();
54 delete self; 61 delete self;
55 } 62 }
56 63
57 private: 64 void addEventListenerData(EventTarget* eventTarget, const AtomicString& even tType, const EventListenerAsyncCallChain& item)
58 friend class AsyncCallStackTracker; 65 {
66 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap>::iterato r it = m_eventTargetCallChains.find(eventTarget);
67 if (it == m_eventTargetCallChains.end())
68 it = m_eventTargetCallChains.set(eventTarget, EventListenerAsyncCall ChainVectorHashMap()).iterator;
69 EventListenerAsyncCallChainVectorHashMap& map = it->value;
70 EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventT ype);
71 if (it2 == map.end())
72 it2 = map.set(eventType, EventListenerAsyncCallChainVector()).iterat or;
73 it2->value.append(item);
74 }
75
76 void removeEventListenerData(EventTarget* eventTarget, const AtomicString& e ventType, const RegisteredEventListener& item)
77 {
78 findEventListenerData(eventTarget, eventType, item, true);
79 }
80
81 PassRefPtr<AsyncCallChain> findEventListenerData(EventTarget* eventTarget, c onst AtomicString& eventType, const RegisteredEventListener& item, bool remove = false)
82 {
83 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap>::iterato r it = m_eventTargetCallChains.find(eventTarget);
84 if (it == m_eventTargetCallChains.end())
85 return 0;
86 EventListenerAsyncCallChainVectorHashMap& map = it->value;
87 EventListenerAsyncCallChainVectorHashMap::iterator it2 = map.find(eventT ype);
88 if (it2 == map.end())
89 return 0;
90 RefPtr<AsyncCallChain> result;
91 EventListenerAsyncCallChainVector& vector = it2->value;
92 for (size_t i = 0; i < vector.size(); ++i) {
93 if (vector[i].first == item) {
94 result = vector[i].second;
95 if (remove) {
96 vector.remove(i);
97 if (vector.isEmpty())
98 map.remove(it2);
99 if (map.isEmpty())
100 m_eventTargetCallChains.remove(it);
101 }
102 break;
103 }
104 }
105 return result.release();
106 }
107
108 public:
59 AsyncCallStackTracker* m_tracker; 109 AsyncCallStackTracker* m_tracker;
60 HashSet<int> m_intervalTimerIds; 110 HashSet<int> m_intervalTimerIds;
61 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains; 111 HashMap<int, RefPtr<AsyncCallChain> > m_timerCallChains;
62 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains; 112 HashMap<int, RefPtr<AsyncCallChain> > m_animationFrameCallChains;
113 HashMap<EventTarget*, EventListenerAsyncCallChainVectorHashMap> m_eventTarge tCallChains;
63 }; 114 };
64 115
65 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames) 116 AsyncCallStackTracker::AsyncCallStack::AsyncCallStack(const String& description, const ScriptValue& callFrames)
66 : m_description(description) 117 : m_description(description)
67 , m_callFrames(callFrames) 118 , m_callFrames(callFrames)
68 { 119 {
69 } 120 }
70 121
71 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack() 122 AsyncCallStackTracker::AsyncCallStack::~AsyncCallStack()
72 { 123 {
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
106 ASSERT(timerId > 0); 157 ASSERT(timerId > 0);
107 ExecutionContextData* data = createContextDataIfNeeded(context); 158 ExecutionContextData* data = createContextDataIfNeeded(context);
108 data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTi meoutName : setIntervalName, callFrames)); 159 data->m_timerCallChains.set(timerId, createAsyncCallChain(singleShot ? setTi meoutName : setIntervalName, callFrames));
109 if (!singleShot) 160 if (!singleShot)
110 data->m_intervalTimerIds.add(timerId); 161 data->m_intervalTimerIds.add(timerId);
111 } 162 }
112 163
113 void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerI d) 164 void AsyncCallStackTracker::didRemoveTimer(ExecutionContext* context, int timerI d)
114 { 165 {
115 ASSERT(context); 166 ASSERT(context);
116 if (!isEnabled() || timerId <= 0) 167 ASSERT(isEnabled());
168 if (timerId <= 0)
117 return; 169 return;
118 ExecutionContextData* data = m_executionContextDataMap.get(context); 170 ExecutionContextData* data = m_executionContextDataMap.get(context);
119 if (!data) 171 if (!data)
120 return; 172 return;
121 data->m_intervalTimerIds.remove(timerId); 173 data->m_intervalTimerIds.remove(timerId);
122 data->m_timerCallChains.remove(timerId); 174 data->m_timerCallChains.remove(timerId);
123 } 175 }
124 176
125 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId ) 177 void AsyncCallStackTracker::willFireTimer(ExecutionContext* context, int timerId )
126 { 178 {
127 ASSERT(context); 179 ASSERT(context);
128 if (!isEnabled()) 180 ASSERT(isEnabled());
129 return;
130 ASSERT(timerId > 0); 181 ASSERT(timerId > 0);
131 ASSERT(!m_currentAsyncCallChain); 182 ASSERT(!m_currentAsyncCallChain);
132 ExecutionContextData* data = m_executionContextDataMap.get(context); 183 ExecutionContextData* data = m_executionContextDataMap.get(context);
133 if (!data) 184 if (!data)
134 return; 185 return;
135 if (data->m_intervalTimerIds.contains(timerId)) 186 if (data->m_intervalTimerIds.contains(timerId))
136 m_currentAsyncCallChain = data->m_timerCallChains.get(timerId); 187 setCurrentAsyncCallChain(data->m_timerCallChains.get(timerId));
137 else 188 else
138 m_currentAsyncCallChain = data->m_timerCallChains.take(timerId); 189 setCurrentAsyncCallChain(data->m_timerCallChains.take(timerId));
139 } 190 }
140 191
141 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames) 192 void AsyncCallStackTracker::didRequestAnimationFrame(ExecutionContext* context, int callbackId, const ScriptValue& callFrames)
142 { 193 {
143 DEFINE_STATIC_LOCAL(String, requestAnimationFrameName, ("requestAnimationFra me")); 194 DEFINE_STATIC_LOCAL(String, requestAnimationFrameName, ("requestAnimationFra me"));
144 195
145 ASSERT(context); 196 ASSERT(context);
146 ASSERT(isEnabled()); 197 ASSERT(isEnabled());
147 if (!validateCallFrames(callFrames)) 198 if (!validateCallFrames(callFrames))
148 return; 199 return;
149 ASSERT(callbackId > 0); 200 ASSERT(callbackId > 0);
150 ExecutionContextData* data = createContextDataIfNeeded(context); 201 ExecutionContextData* data = createContextDataIfNeeded(context);
151 data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(reques tAnimationFrameName, callFrames)); 202 data->m_animationFrameCallChains.set(callbackId, createAsyncCallChain(reques tAnimationFrameName, callFrames));
152 } 203 }
153 204
154 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, i nt callbackId) 205 void AsyncCallStackTracker::didCancelAnimationFrame(ExecutionContext* context, i nt callbackId)
155 { 206 {
156 ASSERT(context); 207 ASSERT(context);
157 if (!isEnabled() || callbackId <= 0) 208 ASSERT(isEnabled());
209 if (callbackId <= 0)
158 return; 210 return;
159 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) 211 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
160 data->m_animationFrameCallChains.remove(callbackId); 212 data->m_animationFrameCallChains.remove(callbackId);
161 } 213 }
162 214
163 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in t callbackId) 215 void AsyncCallStackTracker::willFireAnimationFrame(ExecutionContext* context, in t callbackId)
164 { 216 {
165 ASSERT(context); 217 ASSERT(context);
166 if (!isEnabled()) 218 ASSERT(isEnabled());
167 return;
168 ASSERT(callbackId > 0); 219 ASSERT(callbackId > 0);
169 ASSERT(!m_currentAsyncCallChain); 220 ASSERT(!m_currentAsyncCallChain);
170 if (ExecutionContextData* data = m_executionContextDataMap.get(context)) 221 if (ExecutionContextData* data = m_executionContextDataMap.get(context))
171 m_currentAsyncCallChain = data->m_animationFrameCallChains.take(callback Id); 222 setCurrentAsyncCallChain(data->m_animationFrameCallChains.take(callbackI d));
223 }
224
225 void AsyncCallStackTracker::didAddEventListener(EventTarget* eventTarget, const AtomicString& eventType, EventListener* listener, bool useCapture, const ScriptV alue& callFrames)
226 {
227 ASSERT(eventTarget->executionContext());
228 ASSERT(isEnabled());
229 if (!validateCallFrames(callFrames))
230 return;
231
232 StringBuilder description;
233 description.append(eventTarget->interfaceName());
234 if (!description.isEmpty())
235 description.append(".");
236 if (listener->isAttribute()) {
237 description.append("on");
238 description.append(eventType);
239 } else {
240 description.append("addEventListener(\"");
241 description.append(eventType);
242 description.append("\")");
243 }
244
245 ExecutionContextData* data = createContextDataIfNeeded(eventTarget->executio nContext());
246 data->addEventListenerData(eventTarget, eventType, std::make_pair(Registered EventListener(listener, useCapture), createAsyncCallChain(description.toString() , callFrames)));
247 }
248
249 void AsyncCallStackTracker::didRemoveEventListener(EventTarget* eventTarget, con st AtomicString& eventType, EventListener* listener, bool useCapture)
250 {
251 ASSERT(eventTarget->executionContext());
252 ASSERT(isEnabled());
253 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext()))
254 data->removeEventListenerData(eventTarget, eventType, RegisteredEventLis tener(listener, useCapture));
255 }
256
257 void AsyncCallStackTracker::didRemoveAllEventListeners(EventTarget* eventTarget)
258 {
259 ASSERT(eventTarget->executionContext());
260 ASSERT(isEnabled());
261 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext()))
262 data->m_eventTargetCallChains.remove(eventTarget);
263 }
264
265 void AsyncCallStackTracker::willHandleEvent(EventTarget* eventTarget, const Atom icString& eventType, EventListener* listener, bool useCapture)
266 {
267 ASSERT(eventTarget->executionContext());
268 ASSERT(isEnabled());
269 if (ExecutionContextData* data = m_executionContextDataMap.get(eventTarget-> executionContext()))
270 setCurrentAsyncCallChain(data->findEventListenerData(eventTarget, eventT ype, RegisteredEventListener(listener, useCapture)));
172 } 271 }
173 272
174 void AsyncCallStackTracker::didFireAsyncCall() 273 void AsyncCallStackTracker::didFireAsyncCall()
175 { 274 {
176 m_currentAsyncCallChain = 0; 275 setCurrentAsyncCallChain(0);
177 } 276 }
178 277
179 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA syncCallChain(const String& description, const ScriptValue& callFrames) 278 PassRefPtr<AsyncCallStackTracker::AsyncCallChain> AsyncCallStackTracker::createA syncCallChain(const String& description, const ScriptValue& callFrames)
180 { 279 {
181 ASSERT(isEnabled());
182 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());
183 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1); 281 ensureMaxAsyncCallChainDepth(chain.get(), m_maxAsyncCallStackDepth - 1);
184 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta ck(description, callFrames))); 282 chain->m_callStacks.prepend(adoptRef(new AsyncCallStackTracker::AsyncCallSta ck(description, callFrames)));
185 return chain.release(); 283 return chain.release();
186 } 284 }
187 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
188 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth) 298 void AsyncCallStackTracker::ensureMaxAsyncCallChainDepth(AsyncCallChain* chain, unsigned maxDepth)
189 { 299 {
190 while (chain->m_callStacks.size() > maxDepth) 300 while (chain->m_callStacks.size() > maxDepth)
191 chain->m_callStacks.removeLast(); 301 chain->m_callStacks.removeLast();
192 } 302 }
193 303
194 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames) 304 bool AsyncCallStackTracker::validateCallFrames(const ScriptValue& callFrames)
195 { 305 {
196 return !callFrames.hasNoValue(); 306 return !callFrames.hasNoValue();
197 } 307 }
198 308
199 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex tDataIfNeeded(ExecutionContext* context) 309 AsyncCallStackTracker::ExecutionContextData* AsyncCallStackTracker::createContex tDataIfNeeded(ExecutionContext* context)
200 { 310 {
201 ExecutionContextData* data = m_executionContextDataMap.get(context); 311 ExecutionContextData* data = m_executionContextDataMap.get(context);
202 if (!data) { 312 if (!data) {
203 data = new AsyncCallStackTracker::ExecutionContextData(this, context); 313 data = new AsyncCallStackTracker::ExecutionContextData(this, context);
204 m_executionContextDataMap.set(context, data); 314 m_executionContextDataMap.set(context, data);
205 } 315 }
206 return data; 316 return data;
207 } 317 }
208 318
209 void AsyncCallStackTracker::clear() 319 void AsyncCallStackTracker::clear()
210 { 320 {
211 m_currentAsyncCallChain = 0; 321 m_currentAsyncCallChain = 0;
322 m_nestedAsyncCallCount = 0;
212 ExecutionContextDataMap copy; 323 ExecutionContextDataMap copy;
213 m_executionContextDataMap.swap(copy); 324 m_executionContextDataMap.swap(copy);
214 for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.e nd(); ++it) 325 for (ExecutionContextDataMap::const_iterator it = copy.begin(); it != copy.e nd(); ++it)
215 delete it->value; 326 delete it->value;
216 } 327 }
217 328
218 } // namespace WebCore 329 } // namespace WebCore
OLDNEW
« no previous file with comments | « Source/core/inspector/AsyncCallStackTracker.h ('k') | Source/core/inspector/InspectorDOMDebuggerAgent.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698