apache / jmeter

Apache JMeter open-source load testing tool for analyzing and measuring the performance of a variety of services
https://jmeter.apache.org/
Apache License 2.0
8.25k stars 2.09k forks source link

StackOverflowError with ModuleController in Non-GUI mode if its name is the same as the target node #3180

Open asfimport opened 11 years ago

asfimport commented 11 years ago

christopher.roscoe (Bug 55375): See attached a simple jmx file to reproduce the stackoverflow. There is no problem when i start the test in GUI mode.

In Non-GUI i have the following output.

An error occurred: null errorlevel=1 Drücken Sie eine beliebige Taste . . .

Here is how i start the test in Non-GUI mode.

jmeter -n -t d:\development\workspaces\2.11.3\apache-jmeter-latest\bin\temp\stackoverflow\stackoverflow.jmx -l d:\development\workspaces\2.11.3\apache-jmeter-latest\bin\temp\stackoverflow\stackoverflow.jtl -j d:\development\workspaces\2.11.3\apache-jmeter-latest\bin\temp\stackoverflow\stackoverflow.log

In the log (also attached) the last entry is the stacktrace of the java.lang.StackOverflowError.

2013/08/07 10:04:45 FATAL - jmeter.JMeter: An error occurred: java.lang.StackOverflowError at java.util.HashMap.containsKey(HashMap.java:335) at org.apache.jorphan.collections.ListedHashTree.add(ListedHashTree.java:163) at org.apache.jmeter.control.ModuleController.getReplacementSubTree(ModuleController.java:170) at org.apache.jmeter.JMeter.convertSubTree(JMeter.java:883) at org.apache.jmeter.JMeter.convertSubTree(JMeter.java:885) at org.apache.jmeter.JMeter.convertSubTree(JMeter.java:885) ...

Created attachment stackoverflow.zip: The test to reproduce the error. The log file.

Severity: normal OS: All

Duplicates:

Blocks:

asfimport commented 11 years ago

Sebb (migrated from Bugzilla): Work-round is to rename the Module Controller so it has a different name from the target controller.

May perhaps be related to https://github.com/apache/jmeter/issues/2233?

asfimport commented 11 years ago

Sebb (migrated from Bugzilla): Thanks for the original report and Test Case.

Turned out that the Module Controller (MC) was finding itself rather than the proper target node. Fixed by not allowing the target node to be a Module Controller.

URL: http://svn.apache.org/r1511236 Log: StackOverflowError with ModuleController in Non-GUI mode if its name is the same as the target node https://github.com/apache/jmeter/issues/3180

Added: jmeter/trunk/bin/testfiles/Bug55375.csv (with props) jmeter/trunk/bin/testfiles/Bug55375.jmx (with props) jmeter/trunk/bin/testfiles/Bug55375.xml (with props) Modified: jmeter/trunk/build.xml jmeter/trunk/src/components/org/apache/jmeter/control/ModuleController.java jmeter/trunk/xdocs/changes.xml

asfimport commented 11 years ago

@pmouawad (migrated from Bugzilla): Created attachment stackoverflow.jmx: Test Plan showing another way for issue to occur

stackoverflow.jmx ````xml false true continue false 1 1 0 1344428409000 1344428409000 false groovy "success" false WorkBench Test Plan threadgroup reload false saveConfig true true true true true true false false true false false false false true false false true true 0 true true true true ````
asfimport commented 11 years ago

Sebb (migrated from Bugzilla): It's picking the wrong parent controller.

If you look in the drop-down list in the GUI, there are two instances of the target:

Test Plan > Thread Group > reload

Selecting the second one in the GUI results in a stack overflow error when running the GUI test.

It looks like the non-GUI code happens to choose the wrong target - correct name, wrong instance.

Note: the Test Plans that are created are identical, so the GUI test must be using additional information to identify the target controller.

Not sure if it makes sense to allow multiple MC targets with the same name.

If it does, then the JMX file (and drop-down list) will need to contain extra information to identify the instance. It may just be simpler to complain if the Test Plan contains multiple targets with the same name.

asfimport commented 10 years ago

@pmouawad (migrated from Bugzilla): Created attachment BUG_55375.patch: Patch proposal to detect recursivity

BUG_55375.patch ````diff Index: src/core/org/apache/jmeter/JMeter.java =================================================================== --- src/core/org/apache/jmeter/JMeter.java (revision 1550536) +++ src/core/org/apache/jmeter/JMeter.java (working copy) @@ -82,6 +82,7 @@ import org.apache.jmeter.util.BeanShellServer; import org.apache.jmeter.util.JMeterUtils; import org.apache.jorphan.collections.HashTree; +import org.apache.jorphan.collections.HashTreeTraverser; import org.apache.jorphan.collections.SearchByClass; import org.apache.jorphan.gui.ComponentUtil; import org.apache.jorphan.logging.LoggingManager; @@ -859,6 +860,7 @@ if (subTree != null) { HashTree replacementTree = rc.getReplacementSubTree(); if (replacementTree != null) { + checkForInfiniteRecursivity(replacementTree, rc); convertSubTree(replacementTree); tree.replace(item, rc); tree.set(rc, replacementTree); @@ -884,6 +886,7 @@ if (subTree != null) { HashTree replacementTree = rc.getReplacementSubTree(); if (replacementTree != null) { + checkForInfiniteRecursivity(replacementTree, rc); convertSubTree(replacementTree); tree.replace(item, rc); tree.set(rc, replacementTree); @@ -900,6 +903,72 @@ } } } + + /** + * @since 2.10.1 + */ + private static final class InfiniteRecursivityDetector implements HashTreeTraverser { + boolean infiniteRecursivity; + private ReplaceableController replaceableController; + + /** + * + * @param nodeToFind + */ + public InfiniteRecursivityDetector(ReplaceableController replaceableController) { + this.replaceableController = replaceableController; + } + + /** + * @return boolean + */ + public boolean getInfiniteRecursivity() { + return infiniteRecursivity; + } + + @Override + public void subtractNode() { + // noop + } + + @Override + public void processPath() { + // noop + } + + @Override + public void addNode(Object node, HashTree subTree) { + if(infiniteRecursivity== true) { + return; + } + if(node instanceof JMeterTreeNode) { + Object uo = ((JMeterTreeNode)node).getUserObject(); + if(uo==replaceableController) { + this.infiniteRecursivity = true; + } + } + if(node == replaceableController) { + this.infiniteRecursivity = true; + } + } + } + + /** + * Checks that replacementTree (built after replacement of ReplaceableController) does + * not still contain ReplaceableController which would lead to infinite recursivity + * see BUG 55375 + * @param replacementTree {@link HashTreeTraverser} + * @param replaceableController {@link ReplaceableController} + */ + private static void checkForInfiniteRecursivity(HashTree replacementTree, + final ReplaceableController replaceableController) { + InfiniteRecursivityDetector hashTreeTraverser = new InfiniteRecursivityDetector(replaceableController); + replacementTree.traverse(hashTreeTraverser); + if(hashTreeTraverser.getInfiniteRecursivity()) { + throw new IllegalStateException( + "Recursivity detected in tree:"+((TestElement)replaceableController).getName()); + } + } /** * Ensures the {@link ReplaceableController} is loaded ````
asfimport commented 10 years ago

Sebb (migrated from Bugzilla): The patch effectively converts the stack overflow into an illegal state exception. The benefit is that the message reports the name offending test element (though this may not be unique).

However it does not solve the issue that the non-GUI code behaves differently from the GUI code.

Ideally any fix should address the different behaviour and prevent recursive calls.

If the MC refused to allow multiple controllers with the same name, that should solve the behaviour difference, but it could affect some test plans. Given that at present the non-GUI code does not have sufficient information to choose the correct controller in the case of duplicates, that change is probably acceptable.

The other issue is that the user may choose a target which is a parent of the MC, causing stack overflow. If controller names must be unique then this should be less likely to occur. Since such targets are useless, ideally they should be excluded from the list. This would need to be done after duplicate checking.

asfimport commented 10 years ago

@pmouawad (migrated from Bugzilla): (In reply to Sebb from comment 6)

The patch effectively converts the stack overflow into an illegal state exception. The benefit is that the message reports the name offending test element (though this may not be unique). Shall I commit it ?

However it does not solve the issue that the non-GUI code behaves differently from the GUI code.

In fact both GUI and Non GUI behave identically except that sometimes GUI picks the first element instead of picking the second one. But if you fix it then you get a StackOverflow.

Ideally any fix should address the different behaviour and prevent recursive calls.

Fix prevents recursive calls.

If the MC refused to allow multiple controllers with the same name, that should solve the behaviour difference, but it could affect some test plans. Given that at present the non-GUI code does not have sufficient information to choose the correct controller in the case of duplicates, that change is probably acceptable.

Not sure

The other issue is that the user may choose a target which is a parent of the MC, causing stack overflow. If controller names must be unique then this should be less likely to occur. Since such targets are useless, ideally they should be excluded from the list. This would need to be done after duplicate checking.

Could be next step.

asfimport commented 10 years ago

@pmouawad (migrated from Bugzilla): To be clear, I think the GUI may have in fact changed the saved reference value of Module Controller. So I suspect another bug in GUI mode which gets confused when there are duplicated.

asfimport commented 10 years ago

Sebb (migrated from Bugzilla): I don't see any point in applying a temporary fix.

The GUI seems to remember the correct instance whilst it is active. i.e. it will run which ever is selected from the list.

However, when the JMX is saved, only the name is stored. So when the JMX is reloaded, it cannot possibly know which one was previously selected. This is a fundamental problem for both GUI and non-GUI test runs.

One way to fix this is to insist that controller names are unique. Another way would be to store the instance number. This could either be done for every case, or only done where there are multiple matches, or the instance number could default to the first (i.e. only store it if not the first or only instance).

Requiring unique names would be more likely to break existing test plans, as the default names are not unique, so I now think it would be better to store an instance number. Making the instance default to 1 would reduce the number of test plans that needed to be updated. However it would make it more difficult to determine whether the choice was deliberate or accidental, unless the test plan version is also taken into account. So it might be better to insist that the instance number was always saved.

The question then arises - what should JMeter do if the user adds another controller with the same name after the MC entry has been selected?

asfimport commented 10 years ago

@pmouawad (migrated from Bugzilla): (In reply to Sebb from comment 9)

I don't see any point in applying a temporary fix. Ok, let's wait for a better implementation

The GUI seems to remember the correct instance whilst it is active. i.e. it will run which ever is selected from the list.

However, when the JMX is saved, only the name is stored. So when the JMX is reloaded, it cannot possibly know which one was previously selected. This is a fundamental problem for both GUI and non-GUI test runs. Ok, that's what I was saying.

One way to fix this is to insist that controller names are unique. Another way would be to store the instance number.

Maybe but could be tricky to develop and maintain.

This could either be done for every case, or only done where there are multiple matches, or the instance number could default to the first (i.e. only store it if not the first or only instance).

Requiring unique names would be more likely to break existing test plans, as the default names are not unique, so I now think it would be better to store an instance number. Making the instance default to 1 would reduce the number of test plans that needed to be updated. However it would make it more difficult to determine whether the choice was deliberate or accidental, unless the test plan version is also taken into account. So it might be better to insist that the instance number was always saved.

The question then arises - what should JMeter do if the user adds another controller with the same name after the MC entry has been selected?