WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
Bug 174739
Web Inspector: capture an async stack trace when web content calls addEventListener
https://bugs.webkit.org/show_bug.cgi?id=174739
Summary
Web Inspector: capture an async stack trace when web content calls addEventLi...
Matt Baker
Reported
2017-07-21 18:25:27 PDT
Summary: Capture an async stack trace when web content calls addEventListener. - Add a new WebCore::AsynchronousCallType::EventListener enum value (this enum added in XXXX) - Instrument EventTarget - Add instrumentation end points to PageDebuggerAgent Note: Requires no new UI.
Attachments
Patch
(21.70 KB, patch)
2017-07-21 19:20 PDT
,
Matt Baker
no flags
Details
Formatted Diff
Diff
Patch
(21.97 KB, patch)
2017-07-23 17:55 PDT
,
Matt Baker
no flags
Details
Formatted Diff
Diff
Patch
(22.96 KB, patch)
2017-07-26 18:30 PDT
,
Matt Baker
no flags
Details
Formatted Diff
Diff
Patch
(23.08 KB, patch)
2017-07-28 16:38 PDT
,
Matt Baker
no flags
Details
Formatted Diff
Diff
Show Obsolete
(3)
View All
Add attachment
proposed patch, testcase, etc.
Matt Baker
Comment 1
2017-07-21 19:20:16 PDT
Created
attachment 316160
[details]
Patch
Matt Baker
Comment 2
2017-07-21 19:21:16 PDT
Patch won't apply to TOT until the blocking issue lands.
Radar WebKit Bug Importer
Comment 3
2017-07-21 19:21:38 PDT
<
rdar://problem/33468197
>
Radar WebKit Bug Importer
Comment 4
2017-07-21 19:22:08 PDT
<
rdar://problem/33468189
>
Matt Baker
Comment 5
2017-07-23 17:55:23 PDT
Created
attachment 316244
[details]
Patch
Blaze Burg
Comment 6
2017-07-24 10:08:42 PDT
Comment on
attachment 316244
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=316244&action=review
r- because it needs rebasing.
> Source/WebCore/dom/EventTarget.cpp:-72 > - return ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, options.passive, options.once });
This code seems kinda touchy for performance. Maybe you should check with Ryosuke to learn how to check whether this is a serious perf regression (adding hooks here on the hot path).
> Source/WebCore/dom/EventTarget.cpp:72 > + bool listenerIsScriptDebuggable = listener->type() == EventListener::JSEventListenerType && !listener->wasCreatedFromMarkup();
Move this next to the use site. Also this name is wonky. What about listenerExecutesJavaScript or listenerCanBeDebugged? The !listener->wasCreatedFromMarkup() exclusion deserves some explanation here or in the ChangeLog. Why can't we debug that?
> Source/WebCore/dom/EventTarget.cpp:76 > + if (listenerIsScriptDebuggable) {
To minimize perf impact you should guard the extra work here (iterating the listeners) on InspectorInstrumentation::hasFrontends().
> Source/WebCore/dom/EventTarget.cpp:118 > + auto& listenersForType = eventListeners(eventType);
To minimize perf impact you should guard the extra work here (iterating the listeners) on InspectorInstrumentation::hasFrontends().
> Source/WebCore/dom/EventTarget.cpp:120 > + if (registeredListener->callback() == listener && registeredListener->useCapture() == options.capture) {
What is the purpose of this check for usesCapture?
> Source/WebCore/inspector/InspectorInstrumentation.cpp:354 > +void InspectorInstrumentation::didRegisterEventListenerImpl(InstrumentingAgents& instrumentingAgents, const RegisteredEventListener& listener, ScriptExecutionContext& context)
Applies throughout: please rename to add (so we have add/remove method pairs inside inspector).
> Source/WebCore/inspector/PageDebuggerAgent.cpp:176 > + int listenerIdentifier = m_nextEventListenerIdentifier++;
Please use an unsigned type for the identifier key. Adding a typedef would be nice too, so the HashMap type parameters are a little less vague.
> Source/WebCore/inspector/PageDebuggerAgent.cpp:179 > + didScheduleAsyncCall(scriptState, static_cast<int>(AsynchronousCallType::EventListener), listenerIdentifier, listener.isOnce());
what!? don't cast away the enum type here. Do it inside the methods if they need to use the value for hashing or whatever. Please fix the existing call sites, this is not good.
> Source/WebCore/inspector/PageDebuggerAgent.cpp:184 > + auto eventListenerIterator = m_registeredEventListeners.find(&listener);
Nit: you can just do `auto it = ...`. There's only one iterator, naming it is not helpful.
> Source/WebCore/inspector/PageDebuggerAgent.cpp:195 > + auto eventListenerIterator = m_registeredEventListeners.find(&listener);
Ditto.
Matt Baker
Comment 7
2017-07-25 12:53:28 PDT
Comment on
attachment 316244
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=316244&action=review
`
>> Source/WebCore/dom/EventTarget.cpp:-72 >> - return ensureEventTargetData().eventListenerMap.add(eventType, WTFMove(listener), { options.capture, options.passive, options.once }); > > This code seems kinda touchy for performance. Maybe you should check with Ryosuke to learn how to check whether this is a serious perf regression (adding hooks here on the hot path).
Will investigate. At the very least the call to "auto& listenersForType = ..." should be moved to InspectorInstrumentation and guarded on the presence of frontends.
>> Source/WebCore/dom/EventTarget.cpp:72 >> + bool listenerIsScriptDebuggable = listener->type() == EventListener::JSEventListenerType && !listener->wasCreatedFromMarkup(); > > Move this next to the use site. Also this name is wonky. What about listenerExecutesJavaScript or listenerCanBeDebugged? > > The !listener->wasCreatedFromMarkup() exclusion deserves some explanation here or in the ChangeLog. Why can't we debug that?
I agree this is a poor name. Maybe `listenerCreatedFromScript` would be more appropriate. The reason for ignoring listeners created from markup (<a onclick="didClick()">), is that no script call stack is associated with the event listener's registration.
>> Source/WebCore/dom/EventTarget.cpp:76 >> + if (listenerIsScriptDebuggable) { > > To minimize perf impact you should guard the extra work here (iterating the listeners) on InspectorInstrumentation::hasFrontends().
EventTarget::eventListeners is public, so this step can be moved into InspectorInstrumentation. EventTarget will need to be passed to the instrumentation instead of ScriptExecutionContext (which can be retrieved from the former).
>> Source/WebCore/dom/EventTarget.cpp:120 >> + if (registeredListener->callback() == listener && registeredListener->useCapture() == options.capture) { > > What is the purpose of this check for usesCapture?
The same callback can be registered a second time so long as the `capture` option differs. See EventListenerMap::add.
Matt Baker
Comment 8
2017-07-25 13:51:22 PDT
Comment on
attachment 316244
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=316244&action=review
>> Source/WebCore/inspector/PageDebuggerAgent.cpp:176 >> + int listenerIdentifier = m_nextEventListenerIdentifier++; > > Please use an unsigned type for the identifier key. Adding a typedef would be nice too, so the HashMap type parameters are a little less vague.
The identifier is signed to be consistent with requestAnimationFrame and DOMTimer identifiers handed out in WebCore.
Blaze Burg
Comment 9
2017-07-25 14:05:16 PDT
Comment on
attachment 316244
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=316244&action=review
>>> Source/WebCore/dom/EventTarget.cpp:72 >>> + bool listenerIsScriptDebuggable = listener->type() == EventListener::JSEventListenerType && !listener->wasCreatedFromMarkup(); >> >> Move this next to the use site. Also this name is wonky. What about listenerExecutesJavaScript or listenerCanBeDebugged? >> >> The !listener->wasCreatedFromMarkup() exclusion deserves some explanation here or in the ChangeLog. Why can't we debug that? > > I agree this is a poor name. Maybe `listenerCreatedFromScript` would be more appropriate. The reason for ignoring listeners created from markup (<a onclick="didClick()">), is that no script call stack is associated with the event listener's registration.
Perhaps in a future patch, we could capture the call stack for the operation that creates the associated markup (i.e., the innerHTML setter, DOM mutation APIs, or loading HTML in a particular document).
>>> Source/WebCore/dom/EventTarget.cpp:120 >>> + if (registeredListener->callback() == listener && registeredListener->useCapture() == options.capture) { >> >> What is the purpose of this check for usesCapture? > > The same callback can be registered a second time so long as the `capture` option differs. See EventListenerMap::add.
oh, interesting.
Matt Baker
Comment 10
2017-07-26 18:30:47 PDT
Created
attachment 316504
[details]
Patch
Matt Baker
Comment 11
2017-07-26 18:34:09 PDT
EventTarget instrumentation changed to do no work when no Inspector frontends aren't present.
Matt Baker
Comment 12
2017-07-26 18:35:28 PDT
(In reply to Matt Baker from
comment #11
)
> EventTarget instrumentation changed to do no work when no Inspector > frontends aren't present.
>_<
Only does work when Inspector frontends are present.
Blaze Burg
Comment 13
2017-07-28 10:34:57 PDT
Comment on
attachment 316504
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=316504&action=review
What happens if the page or frame containing the event listeners navigates? I don't see that we are clearing that anywhere.
> Source/WebCore/dom/EventTarget.cpp:71 > + bool listenerCreatedFromScript = listener->type() == EventListener::JSEventListenerType && !listener->wasCreatedFromMarkup();
Move this local next to the use site. It isn't needed prior to the first early exit.
> Source/WebCore/inspector/PageDebuggerAgent.cpp:169 > + auto& listener = eventListeners.last();
Please use the concrete type here. Auto is fine for the collection above.
Matt Baker
Comment 14
2017-07-28 16:07:28 PDT
Comment on
attachment 316504
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=316504&action=review
>> Source/WebCore/dom/EventTarget.cpp:71 >> + bool listenerCreatedFromScript = listener->type() == EventListener::JSEventListenerType && !listener->wasCreatedFromMarkup(); > > Move this local next to the use site. It isn't needed prior to the first early exit.
This needs to come before we WTFMove the listener.
Matt Baker
Comment 15
2017-07-28 16:16:38 PDT
(In reply to Brian Burg from
comment #13
)
> Comment on
attachment 316504
[details]
> Patch > > View in context: >
https://bugs.webkit.org/attachment.cgi?id=316504&action=review
> > What happens if the page or frame containing the event listeners navigates? > I don't see that we are clearing that anywhere.
PageDebuggerAgent overrides didClearAsyncStackData, which is called when the global object is cleared. This is unnecessarily complex — PageDebuggerAgent can just clear its data in didClearMainFrameWindowObject: void PageDebuggerAgent::didClearMainFrameWindowObject() { didClearGlobalObject(); + m_registeredEventListeners.clear(); + m_nextEventListenerIdentifier = 1; }
Matt Baker
Comment 16
2017-07-28 16:23:23 PDT
(In reply to Matt Baker from
comment #15
)
> (In reply to Brian Burg from
comment #13
) > > Comment on
attachment 316504
[details]
> > Patch > > > > View in context: > >
https://bugs.webkit.org/attachment.cgi?id=316504&action=review
> > > > What happens if the page or frame containing the event listeners navigates? > > I don't see that we are clearing that anywhere. > > PageDebuggerAgent overrides didClearAsyncStackData, which is called when the > global object is cleared. This is unnecessarily complex — PageDebuggerAgent > can just clear its data in didClearMainFrameWindowObject: > > void PageDebuggerAgent::didClearMainFrameWindowObject() > { > didClearGlobalObject(); > > + m_registeredEventListeners.clear(); > + m_nextEventListenerIdentifier = 1; > }
Nevermind. The hook `didClearAsyncStackTraceData()` is needed, since there are multiple things in the InspectorDebuggerAgent that can cause async data top be cleared: - Window object cleared - Agent disabled - Change to async stack trace depth setting
Matt Baker
Comment 17
2017-07-28 16:38:29 PDT
Created
attachment 316685
[details]
Patch
Blaze Burg
Comment 18
2017-07-28 17:05:11 PDT
Comment on
attachment 316685
[details]
Patch r=me
WebKit Commit Bot
Comment 19
2017-07-28 17:33:16 PDT
Comment on
attachment 316685
[details]
Patch Clearing flags on attachment: 316685 Committed
r220036
: <
http://trac.webkit.org/changeset/220036
>
WebKit Commit Bot
Comment 20
2017-07-28 17:33:18 PDT
All reviewed patches have been landed. Closing bug.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug