WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
107937
Web Inspector: CPU pegged when inspecting LocalStorage that mutates.
https://bugs.webkit.org/show_bug.cgi?id=107937
Summary
Web Inspector: CPU pegged when inspecting LocalStorage that mutates.
Pavel Feldman
Reported
2013-01-25 04:44:53 PST
Downstream issue:
http://code.google.com/p/chromium/issues/detail?id=172075
Attachments
Patch
(11.81 KB, patch)
2013-02-04 23:59 PST
,
Vivek Galatage
no flags
Details
Formatted Diff
Diff
Patch
(14.90 KB, patch)
2013-02-05 02:45 PST
,
Vivek Galatage
no flags
Details
Formatted Diff
Diff
Patch
(14.79 KB, patch)
2013-02-05 02:50 PST
,
Vivek Galatage
no flags
Details
Formatted Diff
Diff
Patch
(20.59 KB, patch)
2013-02-07 03:37 PST
,
Vivek Galatage
no flags
Details
Formatted Diff
Diff
Patch
(20.51 KB, patch)
2013-02-07 09:47 PST
,
Vivek Galatage
no flags
Details
Formatted Diff
Diff
Show Obsolete
(4)
View All
Add attachment
proposed patch, testcase, etc.
Vivek Galatage
Comment 1
2013-01-29 21:54:34 PST
The issue is with the current implementation of View update. The InspectorDOMStorageAgent fires domStorageUpdated() event for which the view triggers DOMStorage.getEntries. The domStorageUpdated event is fired for every set/remove/clear actions on the localStorage/sessionStorage. So in the test scenario mentioned above, the view update is also triggered for 1000 times. But the operation is expensive one as there are around 1000 entries in the localStorage. So for every call, the marshalling/unmarshalling is carried out, returned as an array back to inspector and then inspector creates a data grid of items. By the time the view is updated for one call, another is queued up to repeat the whole thing. This is very heavy operation as the objects also go through the GC recycle. Hence the inspector stalls or pegs the CPU. I have modified the domStorageUpdated() event with some more additional parameters as: /* * @param {string} storageId * @param {string} storageAction: Could be one of "ItemSet", "ItemRemoved", "ItemsCleared" * @param {string} key * @param {string} oldValue * @param {string} newValue */ domStorageUpdated(storageId, storageAction, key, oldValue, newValue) So now its much more easier to update the view with just the change that occurred within the storage thereby reducing the getEntries calls altogether. When "ItemsCleared" is received, the entire grid is removed. Here are the WIP patches 1. WebKit patch
http://pastebin.com/uaxUnxty
2. Chromium port patch
http://pastebin.com/PpwRZGwN
Please let me know if we can proceed with the above approach to fix the said issue.
Vivek Galatage
Comment 2
2013-01-29 22:03:34 PST
(In reply to
comment #1
)
> Here are the WIP patches > 1. WebKit patch >
http://pastebin.com/uaxUnxty
> > 2. Chromium port patch >
http://pastebin.com/PpwRZGwN
>
The above patches just outline the work done in the backend. Currently I am just logging the domStorageEvent and its associated data in the console on domStorageUpdated(). This will eventually be replaced with proper handling of the action.
Vsevolod Vlasov
Comment 3
2013-01-30 05:09:39 PST
(In reply to
comment #2
)
> (In reply to
comment #1
) > > > > Here are the WIP patches > > 1. WebKit patch > >
http://pastebin.com/uaxUnxty
> > > > 2. Chromium port patch > >
http://pastebin.com/PpwRZGwN
> > > > The above patches just outline the work done in the backend. Currently I am just logging the domStorageEvent and its associated data in the console on domStorageUpdated(). This will eventually be replaced with proper handling of the action.
I didn't look deeply into your patches but sounds like it might be easier to simply add a timeout for localStorage view update (do not update it more than once per second, probably only when this view is shown and focused)
Vivek Galatage
Comment 4
2013-01-30 06:06:51 PST
(In reply to
comment #3
)
> I didn't look deeply into your patches but sounds like it might be easier to simply add a timeout for localStorage view update (do not update it more than once per second, probably only when this view is shown and focused)
Thank you for your feedback. I agree that setting the timeout is much easier than that one presented in the patch. The reason for the above patch is the information about the DOM storage change has almost reached the frontend. (Currently the InspectorDOMStorageAgent::didDispatchDOMStorageEvent() method does receive all the changed key, newValue and oldValue). The only missing thing is exposing this back to the front-end. This way only the delta change will be notified and we need not fetch the whole local/session storage entries again and again. Thereby reducing the round trips. And this will be done, as you have pointed out below, only when the view is in focus. Your thoughts?
Vivek Galatage
Comment 5
2013-01-31 07:27:34 PST
ping?
Vsevolod Vlasov
Comment 6
2013-02-01 04:28:11 PST
(In reply to
comment #5
)
> ping?
(In reply to
comment #4
)
> (In reply to
comment #3
) > > > I didn't look deeply into your patches but sounds like it might be easier to simply add a timeout for localStorage view update (do not update it more than once per second, probably only when this view is shown and focused) > > Thank you for your feedback. I agree that setting the timeout is much easier than that one presented in the patch. > > The reason for the above patch is the information about the DOM storage change has almost reached the frontend. (Currently the InspectorDOMStorageAgent::didDispatchDOMStorageEvent() method does receive all the changed key, newValue and oldValue). The only missing thing is exposing this back to the front-end. This way only the delta change will be notified and we need not fetch the whole local/session storage entries again and again. Thereby reducing the round trips. And this will be done, as you have pointed out below, only when the view is in focus. Your thoughts?
I don't understand why do you need storageAction parameter. It seems to me that the action performed is already defined by key/newValue pair. Key is null => Clear Key is not null, Value is null => Remove Key and Value are not Nulls => Set
Vivek Galatage
Comment 7
2013-02-01 04:32:37 PST
(In reply to
comment #6
)
> I don't understand why do you need storageAction parameter. It seems to me that the action performed is already defined by key/newValue pair. > Key is null => Clear > Key is not null, Value is null => Remove > Key and Value are not Nulls => Set
Yeah perfect. This sounds good to me! And I apologize for doing a bit of over-engineering :)
Vivek Galatage
Comment 8
2013-02-01 04:52:54 PST
(In reply to
comment #6
)
> I don't understand why do you need storageAction parameter. It seems to me that the action performed is already defined by key/newValue pair. > Key is null => Clear > Key is not null, Value is null => Remove > Key and Value are not Nulls => Set
I performed a quick test as: 1. localStorage.setItem("hello","world"); creates a new entry in the local storage. 2. localStorage.setItem("hello",""); 3. localStorage.removeItem("hello"); Now for 2 and 3, we get the return values as key: "hello" newValue: "" oldValue: "world" So in this case we wont be able to distinguish which operation was performed.
Vsevolod Vlasov
Comment 9
2013-02-01 05:52:11 PST
(In reply to
comment #8
)
> (In reply to
comment #6
) > > I don't understand why do you need storageAction parameter. It seems to me that the action performed is already defined by key/newValue pair. > > Key is null => Clear > > Key is not null, Value is null => Remove > > Key and Value are not Nulls => Set > > I performed a quick test as: > > 1. localStorage.setItem("hello","world"); > creates a new entry in the local storage. > > 2. localStorage.setItem("hello",""); > 3. localStorage.removeItem("hello"); > > Now for 2 and 3, we get the return values as > key: "hello" > newValue: "" > oldValue: "world" > > So in this case we wont be able to distinguish which operation was performed.
We do not distinguish empty and null strings in the inspector protocol. You can still distinguish this cases on the back-end though. I'm fine with adding separate inspector protocol method for each case.
Vivek Galatage
Comment 10
2013-02-04 23:59:02 PST
Created
attachment 186560
[details]
Patch
Vivek Galatage
Comment 11
2013-02-05 00:54:09 PST
(In reply to
comment #10
)
> Created an attachment (id=186560) [details] > Patch
The functionality test is available at LayoutTests/inspector/storage-panel-dom-storage-update.html I think we need to add the protocol test. But this is not a public protocol api/event yet. So shall we add that test to LayoutTests/inspector-protocol ?
Pavel Feldman
Comment 12
2013-02-05 01:54:58 PST
Comment on
attachment 186560
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=186560&action=review
> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:71 > + var rootNode = this._dataGrid.rootNode();
You should not be doing this all when the view is not visible.
> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:80 > + if (childNode.data.key === key && childNode.data.value === oldValue) {
Why do you need to compare the old values? If key is getting removed, it does not matter what value it had.
> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:88 > + rootNode.appendChild(childNode);
This is still going to be slow if localstorage is being populated within a loop. I wonder if it makes sense to send keys only and request values lazily. Could be a part of a separate change though.
> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:94 > + if (childNode.data.key === key && childNode.data.value === oldValue) {
ditto
> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:104 > update: function()
This mo longer needs to be public.
> Source/WebCore/inspector/front-end/ResourcesPanel.js:120 > + WebInspector.domStorageModel.addEventListener(WebInspector.DOMStorageModel.Events.DOMStorageChanged, this._domStorageChanged, this);
While you are here, it might make sense to move the listener registration from the panel into the views.
Vivek Galatage
Comment 13
2013-02-05 02:06:35 PST
Comment on
attachment 186560
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=186560&action=review
Thank you for your review.
>> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:71 >> + var rootNode = this._dataGrid.rootNode(); > > You should not be doing this all when the view is not visible.
This check is done prior to calling onChange in ResourcesPanel::_domStorageChanged(). Hence I did avoid calling it again here.
>> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:80 >> + if (childNode.data.key === key && childNode.data.value === oldValue) { > > Why do you need to compare the old values? If key is getting removed, it does not matter what value it had.
Agree. Will remove it.
>> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:88 >> + rootNode.appendChild(childNode); > > This is still going to be slow if localstorage is being populated within a loop. I wonder if it makes sense to send keys only and request values lazily. Could be a part of a separate change though.
hmm I am not quite sure why would it be slow in a loop. Can you please let me know what is that I am missing? As the event, "DOMStorageChanged", is fired every time there's a change in the DOM storage, the value associated with the key is also sent alongwith.
>> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:104 >> update: function() > > This mo longer needs to be public.
Agree. Will change it.
>> Source/WebCore/inspector/front-end/ResourcesPanel.js:120 >> + WebInspector.domStorageModel.addEventListener(WebInspector.DOMStorageModel.Events.DOMStorageChanged, this._domStorageChanged, this); > > While you are here, it might make sense to move the listener registration from the panel into the views.
In that case the view needs to know about the model. I think the current registration mechanism shields the view from having the knowledge of the model. Also the panel has control over the views(i.e. which view is currently visible and which of them are not).
Vivek Galatage
Comment 14
2013-02-05 02:45:15 PST
Created
attachment 186585
[details]
Patch
Vivek Galatage
Comment 15
2013-02-05 02:50:07 PST
Created
attachment 186588
[details]
Patch
Vivek Galatage
Comment 16
2013-02-05 02:54:09 PST
Comment on
attachment 186588
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=186588&action=review
> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:77 > + this.deleteButton.visible = false;
The delete button's visibility should be handled as well.
> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:187 > },
The above lines were causing the view update being called twice by explicitly calling update() and the other being triggered by the DOM Storage Agent. This is unnecessary hence removing it.
> Source/WebCore/inspector/front-end/DOMStorageItemsView.js:-149 > - this.update();
ditto
Build Bot
Comment 17
2013-02-05 05:50:34 PST
Comment on
attachment 186588
[details]
Patch
Attachment 186588
[details]
did not pass win-ews (win): Output:
http://queues.webkit.org/results/16368679
Vivek Galatage
Comment 18
2013-02-05 06:37:28 PST
(In reply to
comment #17
)
> (From update of
attachment 186588
[details]
) >
Attachment 186588
[details]
did not pass win-ews (win): > Output:
http://queues.webkit.org/results/16368679
This change is not related to the build failure observed on win ews.
Vivek Galatage
Comment 19
2013-02-06 00:09:38 PST
ping r?
Yury Semikhatsky
Comment 20
2013-02-07 02:01:18 PST
Comment on
attachment 186588
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=186588&action=review
> Source/WebCore/inspector/Inspector.json:1500 > + { "name": "oldValue", "type": "string" },
oldValue and newValue should be optional as they makes sense not for all actions. Even key param should be optional as there is no key for itemsCleared. Vsevolod's suggestion to split the event into 4 would eliminate the problem.
> Source/WebCore/inspector/InspectorDOMStorageAgent.cpp:238 > + if (key.isEmpty() && key.isNull())
isNull implies isEmpty
> Source/WebCore/inspector/InspectorDOMStorageAgent.cpp:240 > + else if (newValue.isEmpty() && newValue.isNull())
ditto
> Source/WebCore/inspector/InspectorDOMStorageAgent.cpp:242 > + else if (oldValue.isEmpty() && oldValue.isNull())
ditto
Vivek Galatage
Comment 21
2013-02-07 03:37:27 PST
Created
attachment 187045
[details]
Patch
WebKit Review Bot
Comment 22
2013-02-07 08:27:13 PST
Comment on
attachment 187045
[details]
Patch Rejecting
attachment 187045
[details]
from commit-queue. Failed to run "['/mnt/git/webkit-commit-queue/Tools/Scripts/webkit-patch', '--status-host=queues.webkit.org', '--bot-id=gce-cq-04', 'land-attachment', '--force-clean', '--non-interactive', '--parent-command=commit-queue', 187045, '--port=chromium-xvfb']" exit_code: 2 cwd: /mnt/git/webkit-commit-queue Last 500 characters of output: commit-queue/Source/WebKit/chromium/third_party/v8-i18n --revision 164 --non-interactive --force --accept theirs-conflict --ignore-externals' in '/mnt/git/webkit-commit-queue/Source/WebKit/chromium' 54>At revision 164. ________ running '/usr/bin/python tools/clang/scripts/update.py --mac-only' in '/mnt/git/webkit-commit-queue/Source/WebKit/chromium' ________ running '/usr/bin/python gyp_webkit' in '/mnt/git/webkit-commit-queue/Source/WebKit/chromium' Updating webkit projects from gyp files... Full output:
http://queues.webkit.org/results/16435214
Vivek Galatage
Comment 23
2013-02-07 09:47:03 PST
Created
attachment 187129
[details]
Patch
WebKit Review Bot
Comment 24
2013-02-07 12:04:16 PST
Comment on
attachment 187129
[details]
Patch Clearing flags on attachment: 187129 Committed
r142161
: <
http://trac.webkit.org/changeset/142161
>
WebKit Review Bot
Comment 25
2013-02-07 12:04:23 PST
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