Bug 92198 - Web Inspector: debugging long-running scripts with conditional breakpoints consumes huge amounts of memory
Summary: Web Inspector: debugging long-running scripts with conditional breakpoints co...
Status: UNCONFIRMED
Alias: None
Product: WebKit
Classification: Unclassified
Component: Web Inspector (show other bugs)
Version: 528+ (Nightly build)
Hardware: All All
: P2 Normal
Assignee: Nobody
URL:
Keywords: InRadar
Depends on:
Blocks:
 
Reported: 2012-07-24 20:10 PDT by Félix Cloutier
Modified: 2016-12-13 15:36 PST (History)
14 users (show)

See Also:


Attachments
The test case (682 bytes, text/html)
2012-07-24 20:10 PDT, Félix Cloutier
no flags Details
Patch (4.84 KB, patch)
2012-12-13 00:37 PST, Peter Wang
yurys: review-
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Félix Cloutier 2012-07-24 20:10:06 PDT
When uninterrupted, long-running scripts execute under the Javascript debugger, and those scripts have conditional breakpoints, WebProcess will consume huge amounts of memory for seemingly little reason.

On the test case attached, with the conditional breakpoints, memory consumption continually grows to the point where there's no more free memory available. With the conditional breakpoints off, memory usage remains about stable.

STEPS TO REPRODUCE
1. Launch the attached web page
2. Open the Web inspector, start the debugger
3. Add 4 conditional breakpoints, one per line inside the `for` loop of the `foo` function, and give them an unlikely condition (`i == -1` for instance)
4. With your favorite diagnostics tool, watch WebProcess eat up all the memory it can
Comment 1 Félix Cloutier 2012-07-24 20:10:35 PDT
Created attachment 154228 [details]
The test case
Comment 2 Peter Wang 2012-12-13 00:28:10 PST
This problem is more obvious in memory-limited environment (e.g. mobile).

The root reason is that, for failed condition of breakpoint, JSC doesn't run the EventLoop, so in a dense loop of js code, there is no chance to recycle garbage caused by evaluating the breakpoint's condition.

There is an interesting test. if you put a normal breakpoint after the breakpoint with failed condition, the GC is working well. Since for a success breakpoint JSC run an EventLoop.
Comment 3 Peter Wang 2012-12-13 00:37:24 PST
Created attachment 179222 [details]
Patch
Comment 4 Yury Semikhatsky 2012-12-13 00:49:50 PST
Comment on attachment 179222 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=179222&action=review

> Source/WebCore/ChangeLog:11
> +        No new test case.

Can you add a layout test for this? Since the problem is on the back-end you can add a protocol test for it(LayoutTests/inspector-protocol)
Comment 5 Peter Wang 2012-12-13 01:04:03 PST
(In reply to comment #4)
> (From update of attachment 179222 [details])
> View in context: https://bugs.webkit.org/attachment.cgi?id=179222&action=review
> 
> > Source/WebCore/ChangeLog:11
> > +        No new test case.
> 
> Can you add a layout test for this? Since the problem is on the back-end you can add a protocol test for it(LayoutTests/inspector-protocol)

ok. Let me a try.
Comment 6 Yury Semikhatsky 2012-12-13 01:19:46 PST
(In reply to comment #2)
> This problem is more obvious in memory-limited environment (e.g. mobile).
> 
> The root reason is that, for failed condition of breakpoint, JSC doesn't run the EventLoop, so in a dense loop of js code, there is no chance to recycle garbage caused by evaluating the breakpoint's condition.
> 
It sounds weird that the issue is specific to breakpoint conditions. How does it work in case of eval producing a lot of garbage in a tight loop in user code?



> There is an interesting test. if you put a normal breakpoint after the breakpoint with failed condition, the GC is working well. Since for a success breakpoint JSC run an EventLoop.
Comment 7 Peter Wang 2012-12-13 18:55:24 PST
(In reply to comment #6)
> (In reply to comment #2)
> > This problem is more obvious in memory-limited environment (e.g. mobile).
> > 
> > The root reason is that, for failed condition of breakpoint, JSC doesn't run the EventLoop, so in a dense loop of js code, there is no chance to recycle garbage caused by evaluating the breakpoint's condition.
> > 
> It sounds weird that the issue is specific to breakpoint conditions. How does it work in case of eval producing a lot of garbage in a tight loop in user code?
> 
Sorry to reply so late, since I spent some time to investigate the related code of JSC. For the code like this:
for (var i=0;i<0xfffff;i++) {
...
eval("...");
...
}
The "EvalExecutable" is allocated just once and saved as an argument of current callFrame (Interpreter.cpp:149).

But to evaluate the condition of breakpoint, the DebuggerCallFrame::evaluate is invoked, the "EvalExecutable" is allocated every time. Maybe, because it's difficult to bind the breakpoint-condition's string with a certain callFrame.
Comment 8 Yury Semikhatsky 2012-12-14 01:02:09 PST
(In reply to comment #7)
> (In reply to comment #6)
> > (In reply to comment #2)
> > > This problem is more obvious in memory-limited environment (e.g. mobile).
> > > 
> > > The root reason is that, for failed condition of breakpoint, JSC doesn't run the EventLoop, so in a dense loop of js code, there is no chance to recycle garbage caused by evaluating the breakpoint's condition.
> > > 
> > It sounds weird that the issue is specific to breakpoint conditions. How does it work in case of eval producing a lot of garbage in a tight loop in user code?
> > 
> Sorry to reply so late, since I spent some time to investigate the related code of JSC. For the code like this:
> for (var i=0;i<0xfffff;i++) {
> ...
> eval("...");
> ...
> }
> The "EvalExecutable" is allocated just once and saved as an argument of current callFrame (Interpreter.cpp:149).
> 
> But to evaluate the condition of breakpoint, the DebuggerCallFrame::evaluate is invoked, the "EvalExecutable" is allocated every time. Maybe, because it's difficult to bind the breakpoint-condition's string with a certain callFrame.

Why JSC cannot run GC automatically during the breakpoint condition evaluation if it needs to free some memory? We need someone who understands JSC garbage collector details to look at this change.
Comment 9 Peter Wang 2012-12-14 01:16:49 PST
(In reply to comment #8)
> (In reply to comment #7)
> > (In reply to comment #6)
> > > (In reply to comment #2)
> > > > This problem is more obvious in memory-limited environment (e.g. mobile).
> > > > 
> > > > The root reason is that, for failed condition of breakpoint, JSC doesn't run the EventLoop, so in a dense loop of js code, there is no chance to recycle garbage caused by evaluating the breakpoint's condition.
> > > > 
> > > It sounds weird that the issue is specific to breakpoint conditions. How does it work in case of eval producing a lot of garbage in a tight loop in user code?
> > > 
> > Sorry to reply so late, since I spent some time to investigate the related code of JSC. For the code like this:
> > for (var i=0;i<0xfffff;i++) {
> > ...
> > eval("...");
> > ...
> > }
> > The "EvalExecutable" is allocated just once and saved as an argument of current callFrame (Interpreter.cpp:149).
> > 
> > But to evaluate the condition of breakpoint, the DebuggerCallFrame::evaluate is invoked, the "EvalExecutable" is allocated every time. Maybe, because it's difficult to bind the breakpoint-condition's string with a certain callFrame.
> 
> Why JSC cannot run GC automatically during the breakpoint condition evaluation if it needs to free some memory? We need someone who understands JSC garbage collector details to look at this change.

I'm not expert of it. It depends on the strategy of port, usually JSC does GC with a interval-changeable timer. So need to run EventLoop to handle the timer message.
I believe the design of JSC takes repeat "eval()" into account, but the mechanism of debugger works-around it.

Welcome JSC's experts to have a look.
Comment 10 Brian Burg 2014-11-29 19:18:15 PST
This still repros on TOT.
Comment 11 Radar WebKit Bug Importer 2014-11-29 19:18:31 PST
<rdar://problem/19096505>