Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 301438

Summary: Eclipse hangs when attempting to refactor using the "change method signature"
Product: [Eclipse Project] JDT Reporter: Steve Garbarini <sgarbarini>
Component: CoreAssignee: Satyam Kandula <satyam.kandula>
Status: VERIFIED FIXED QA Contact:
Severity: critical    
Priority: P3 CC: daniel_megert, frederic_fusier, jarthana, markus.kell.r, Olivier_Thomann, satyam.kandula, srikanth_sankaran
Version: 3.5.1Flags: daniel_megert: pmc_approved+
srikanth_sankaran: review+
markus.kell.r: review+
Target Milestone: 3.5.2   
Hardware: PC   
OS: Linux   
Whiteboard:
Attachments:
Description Flags
Stack trace when I "break" the debugger during Eclipse hang
none
Zip of a project that demonstrates the slowness of the refactor operation
none
Proposed patch
none
Test patch
none
Proposed patch for 3.5 maintenance stream
none
Patch on 3.5 maintenance branch
satyam.kandula: review?
Patch on Head satyam.kandula: review?

Description Steve Garbarini CLA 2010-02-01 12:16:50 EST
Build Identifier: M20090917-0800

In a particular case, when refactoring and using the "change method signature" option Eclipse hangs (immediately... no dialog pops up). It only seems to happen when refactoring some methods in a particular source file.  Unfortunately, I cannot isolate the problem with a small example and I cannot provide the source file in question.  I've used Eclipse to debug Eclipse and if I "break" I commonly see the attached stack trace.


Reproducible: Always

Steps to Reproduce:
1. Select a method in a particular source file
2. Use the "change method signature" refactoring option
3. Eclipse hangs immediately
Comment 1 Steve Garbarini CLA 2010-02-01 12:18:20 EST
Created attachment 157805 [details]
Stack trace when I "break" the debugger during Eclipse hang
Comment 2 Frederic Fusier CLA 2010-02-01 12:39:59 EST
The problem is typically highlighted by your code, hence it would be really easier for us to figure out what happens there with an example.

As you can debug it and break in the offending loop, you can step in the debugger and then get all information from the stack trace. So, you could have a more precise idea about the code area where the Scanner seems to become crazy. Then hopefully, you should be able to extract a small sample and put it in this bug to help us to reproduce this issue...

TIA
Comment 3 Olivier Thomann CLA 2010-02-01 12:42:16 EST
I don't see an infinite loop in this code (org.eclipse.jdt.internal.compiler.parser.Scanner.checkTaskTag(int, int)).

Do you have task tags in the corresponding file ? If you could at least give us the code where the scanner is running checkTaskTag, this could be really helpful.

As is, it is difficult to see what could be wrong. I can do remote debugging if required.
Comment 4 Steve Garbarini CLA 2010-02-01 13:38:12 EST
I'll see what I can do to narrow it down.  I guess I need to download the source code to do effective debugging, yes?

I "broke" into the debugger several more times... the common method seems to be:

Parser.checkNonNLSAfterBodyEnd(int) line: 1139	

so maybe it is possible that that method is in an infinite loop?
Comment 5 Olivier Thomann CLA 2010-02-01 13:49:05 EST
Possibly.

The code is doing:
protected void checkNonNLSAfterBodyEnd(int declarationEnd){
	if(this.scanner.currentPosition - 1 <= declarationEnd) {
		this.scanner.eofPosition = declarationEnd < Integer.MAX_VALUE ? declarationEnd + 1 : declarationEnd;
		try {
			while(this.scanner.getNextToken() != TokenNameEOF){/*empty*/}
		} catch (InvalidInputException e) {
			// Nothing to do
		}
	}
}

So this would mean that somehow we never end up returning a TokenNameEOF token. This can technically be an infinite loop, but I would like to understand why the scanner is initialized in a way that never returns a token TokenNameEOF.
So a regression test would be welcome.

Do you have syntax errors ?
Comment 6 Steve Garbarini CLA 2010-02-01 17:38:58 EST
I have no syntax errors and, as it turns out, Eclipse was not hung, so periodically "breaking" the debugger just showed me where the code was most of the time (and, of course, it was in getNextToken or thereabouts).

I finally downloaded the source code so I could debug effectively.  The code was looping through 926 classes in:

MethodChecks.isDeclaredInInterface(IMethod method, ITypeHierarchy hierarchy, IProgressMonitor monitor)

and it took aver 20 minutes to return from that method.  As I write this comment the code now seems "hung" again because the "Checking preconditions..." dialog is sitting there with no progress.  I assume if I leave it long enough that it will return.  This is on a Core i7-860 where 1GB was alloted to Eclipse.

I'm an Eclipse fan, but Netbeans was able to perform the entire refactor in less than 5 minutes (and it showed progress whenever the system was busy... Eclipse did not and that is one of the reasons I thought it was hung).

Anyway, thanks for being interested in the problem.
Comment 7 Olivier Thomann CLA 2010-02-01 21:41:40 EST
(In reply to comment #6)
> I'm an Eclipse fan, but Netbeans was able to perform the entire refactor in
> less than 5 minutes (and it showed progress whenever the system was busy...
> Eclipse did not and that is one of the reasons I thought it was hung).
I'd like to debug it myself so that I can try to understand why it is looping. Now as I understood, it is not possible to get access to the source code.

So unless you can provide reproducable steps, I don't see how we can proceed.
Comment 8 Steve Garbarini CLA 2010-02-02 01:12:04 EST
I've tried to model our real code with some more simplistic code.  I've created something that is slow (30 seconds before there is any indication that Eclipse isn't hung), but it is a far cry from 20 minutes.  I think the problem has to do with large numbers of inner classes (it is a method in the base class of all those inner classes that is getting refactored).  If I'm understanding the code right, these multiple inner classes cause the outer class to get parsed multiple times.  Anyway, see the attached zip field and try to refactor (change the method signature of) Foo.method1.
Comment 9 Steve Garbarini CLA 2010-02-02 01:13:13 EST
Created attachment 157869 [details]
Zip of a project that demonstrates the slowness of the refactor operation
Comment 10 Satyam Kandula CLA 2010-02-02 08:11:43 EST
I got a similar call stack with the file GenericTypeTest of jdt core test. Open this file, right click on this element on the package explorer and try to click on Run As.
Comment 11 Olivier Thomann CLA 2010-02-03 20:42:00 EST
Satyam, please investigate.
Comment 12 Satyam Kandula CLA 2010-02-09 23:30:44 EST
Created attachment 158662 [details]
Proposed patch

Parsing the methods which is not necessary is causing the slowdown. A part of the fix for bug 254738 is causing the un-necessary parsing of the methods. This part is actually not required because of the subsequent fix for bug 288621. So, I removed those unnecessary changes.
Comment 13 Satyam Kandula CLA 2010-02-10 03:43:22 EST
Created attachment 158672 [details]
Test patch

Test to demonstrate the time taken with and without the patch. I don't think it is necessary to put it in the regression test.
Comment 14 Satyam Kandula CLA 2010-02-10 03:45:22 EST
(In reply to comment #12)
> This part is actually not required because of the subsequent fix for bug 288621. 

This is my mistake, 288621 shouldn't have any impact. However, the changes that were done earlier are not necessary and hence they can be removed as stated in the attached patch.
Comment 15 Dani Megert CLA 2010-02-11 05:20:58 EST
This is indeed a critical regression that got introduced into R3.5.1 and a must fix for 3.5.2.

Approving to fix for 3.5.2.
Comment 16 Satyam Kandula CLA 2010-02-11 05:40:17 EST
Created attachment 158831 [details]
Proposed patch for 3.5 maintenance stream
Comment 17 Satyam Kandula CLA 2010-02-11 05:42:32 EST
Refactorings and selection of "Run As" in the context menu are most impacted.
The impact varies depending on the size of the file! 

Basically calls to newSupertypeHierarchy() on a type that has the CU open will
experience slowdown. Method statements which weren't getting parsed of an
already open CU was getting parsed causing the slowdown.  

Here are some performance numbers of the call to newSupertypeHierarchy() on some JDT/core source files:

Parser.java: ~10K lines
3.5 - ~12 ms
3.5.2 - ~1000 seconds
3.5.2 with fix - ~12 ms

HierarchyResolver.java : ~800 lines
3.5 - ~3 ms 
3.5.2 - ~15 ms
3.5.2 with fix - ~3 ms

JavaCore.java: ~5K lines 
3.5  - ~12 ms
3.5.2 - ~334 ms
3.5.2 with fix - ~12 ms
Comment 18 Srikanth Sankaran CLA 2010-02-11 07:20:00 EST
(In reply to comment #12)
> Created an attachment (id=158662) [details]
> Proposed patch
> 
> Parsing the methods which is not necessary is causing the slowdown. A part of
> the fix for bug 254738 is causing the un-necessary parsing of the methods. This
> part is actually not required because of the subsequent fix for bug 288621. So,
> I removed those unnecessary changes.

I agree with the change. But could you please add a suitable comment
at the point in source. If it was mistaken to be superfluous once,
it can be mistaken again too.
Comment 19 Satyam Kandula CLA 2010-02-11 07:40:52 EST
I meant 1000 ms not 1000 seconds for Parser.java on 3.5.2
(In reply to comment #17) 
> Refactorings and selection of "Run As" in the context menu are most impacted.
> The impact varies depending on the size of the file! 
> 
> Basically calls to newSupertypeHierarchy() on a type that has the CU open will
> experience slowdown. Method statements which weren't getting parsed of an
> already open CU was getting parsed causing the slowdown.  
> 
> Here are some performance numbers of the call to newSupertypeHierarchy() on
> some JDT/core source files:
> 
> Parser.java: ~10K lines
> 3.5 - ~12 ms
> 3.5.2 - ~1000 seconds
> 3.5.2 with fix - ~12 ms
> 
> HierarchyResolver.java : ~800 lines
> 3.5 - ~3 ms 
> 3.5.2 - ~15 ms
> 3.5.2 with fix - ~3 ms
> 
> JavaCore.java: ~5K lines 
> 3.5  - ~12 ms
> 3.5.2 - ~334 ms
> 3.5.2 with fix - ~12 ms
Comment 20 Satyam Kandula CLA 2010-02-11 07:55:52 EST
For the attached testcase, call to newSupertypeHieararchy() on one of the subtypes of Foo() in Bar1 takes 400 ms with 352 and 4ms with 3.5 and the patch.
Comment 21 Markus Keller CLA 2010-02-11 08:07:01 EST
(In reply to comment #16)
> Created an attachment (id=158831) [details] [diff]
> Proposed patch for 3.5 maintenance stream

+1 for this patch. The patch from comment 12 did too much.
Comment 22 Satyam Kandula CLA 2010-02-11 08:38:39 EST
With 3.5.2 "Change method signature" on the test case provided takes around 4
mins for the dialog box to come up. Whereas with 3.5 it just takes less than 8
seconds. With the attached patch also, it takes less than 8 seconds.
Comment 23 Satyam Kandula CLA 2010-02-11 08:51:36 EST
Created attachment 158845 [details]
Patch on 3.5 maintenance branch

Added a comment as Srikanth had mentioned in comment 18.
Comment 24 Satyam Kandula CLA 2010-02-11 08:56:32 EST
Created attachment 158846 [details]
Patch on Head

Made the changes similar to the one on 3.5 maintenance.
Comment 25 Olivier Thomann CLA 2010-02-11 10:10:41 EST
Released for 3.5.2.
Comment 26 Olivier Thomann CLA 2010-02-11 12:10:12 EST
Released for 3.6M6 with the performance regression test.
Comment 27 Olivier Thomann CLA 2010-02-11 12:12:52 EST
I released the regression test in the perf_35x branch.
Comment 28 Jay Arthanareeswaran CLA 2010-03-08 03:42:18 EST
Verified for 3.6M6 using build I20100305-1011
Comment 29 Frederic Fusier CLA 2010-05-11 05:30:39 EDT
*** Bug 298948 has been marked as a duplicate of this bug. ***