Dzmitry Hubin
Dzmitry Hubin

Reputation: 1301

Sonar maven plugin hangs during Java Main Files AST scan

I'm using sonar maven plugin to trigger java code analysis.

Sonar-runner gets stuck with one java file processing. The last message on the console reads Java AST Scan and process gets stuck at that..

SonarQube version: 7.3.0

Sonar-maven-plugin version: 3.6.0.1398 (latest one) but tried with 3.4.1.1168 as well

Logs look like this:

[INFO] Java Main Files AST scan
[INFO] 12/41 files analyzed, current file: {path-to-file}/Foo.java
[INFO] 12/41 files analyzed, current file: {path-to-file}/Foo.java
[INFO] 12/41 files analyzed, current file: {path-to-file}/Foo.java
[INFO] 12/41 files analyzed, current file: {path-to-file}/Foo.java
...

OOM exception stacktrace:

12:30:17 [SonarQube analysis] [ERROR] Java heap space -> [Help 1]
12:30:17 [SonarQube analysis] java.lang.OutOfMemoryError: Java heap space
12:30:17 [SonarQube analysis]     at org.sonar.java.collections.AVLTree$Equals.compute (AVLTree.java:455)
12:30:17 [SonarQube analysis]     at org.sonar.java.collections.AVLTree$Node.equals (AVLTree.java:387)
12:30:17 [SonarQube analysis]     at java.util.Objects.equals (Objects.java:77)
12:30:17 [SonarQube analysis]     at org.sonar.java.se.ProgramState.equals (ProgramState.java:260)
12:30:17 [SonarQube analysis]     at java.util.Objects.equals (Objects.java:77)
12:30:17 [SonarQube analysis]     at org.sonar.java.se.ExplodedGraph$Node.equals (ExplodedGraph.java:124)
12:30:17 [SonarQube analysis]     at java.util.HashMap$TreeNode.find (HashMap.java:1919)
12:30:17 [SonarQube analysis]     at java.util.HashMap$TreeNode.find (HashMap.java:1929)
12:30:17 [SonarQube analysis]     at java.util.HashMap$TreeNode.putTreeVal (HashMap.java:2048)
12:30:17 [SonarQube analysis]     at java.util.HashMap.putVal (HashMap.java:638)
12:30:17 [SonarQube analysis]     at java.util.HashMap.put (HashMap.java:612)
12:30:17 [SonarQube analysis]     at org.sonar.java.se.ExplodedGraph.node (ExplodedGraph.java:55)
12:30:17 [SonarQube analysis]     at org.sonar.java.se.ExplodedGraphWalker.enqueue (ExplodedGraphWalker.java:1101)
12:30:17 [SonarQube analysis]     at org.sonar.java.se.ExplodedGraphWalker.enqueue (ExplodedGraphWalker.java:1083)
12:30:17 [SonarQube analysis]     at org.sonar.java.se.ExplodedGraphWalker.enqueue (ExplodedGraphWalker.java:1075)
12:30:17 [SonarQube analysis]     at org.sonar.java.se.ExplodedGraphWalker.execute (ExplodedGraphWalker.java:231)
12:30:17 [SonarQube analysis]     at org.sonar.java.se.ExplodedGraphWalker.visitMethod (ExplodedGraphWalker.java:209)
12:30:17 [SonarQube analysis]     at org.sonar.java.se.SymbolicExecutionVisitor.execute (SymbolicExecutionVisitor.java:74)
12:30:17 [SonarQube analysis]     at org.sonar.java.se.SymbolicExecutionVisitor.visitNode (SymbolicExecutionVisitor.java:64)
12:30:17 [SonarQube analysis]     at org.sonar.java.ast.visitors.SubscriptionVisitor.visit (SubscriptionVisitor.java:103)
12:30:17 [SonarQube analysis]     at org.sonar.java.ast.visitors.SubscriptionVisitor.visitChildren (SubscriptionVisitor.java:128)
12:30:17 [SonarQube analysis]     at org.sonar.java.ast.visitors.SubscriptionVisitor.visit (SubscriptionVisitor.java:105)
12:30:17 [SonarQube analysis]     at org.sonar.java.ast.visitors.SubscriptionVisitor.visitChildren (SubscriptionVisitor.java:128)
12:30:17 [SonarQube analysis]     at org.sonar.java.ast.visitors.SubscriptionVisitor.visit (SubscriptionVisitor.java:105)
12:30:17 [SonarQube analysis]     at org.sonar.java.ast.visitors.SubscriptionVisitor.scanTree (SubscriptionVisitor.java:86)
12:30:17 [SonarQube analysis]     at org.sonar.java.ast.visitors.SubscriptionVisitor.scanFile (SubscriptionVisitor.java:72)
12:30:17 [SonarQube analysis]     at org.sonar.java.se.SymbolicExecutionVisitor.scanFile (SymbolicExecutionVisitor.java:54)
12:30:17 [SonarQube analysis]     at org.sonar.java.model.VisitorsBridge.runScanner (VisitorsBridge.java:148)
12:30:17 [SonarQube analysis]     at org.sonar.java.model.VisitorsBridge.visitFile (VisitorsBridge.java:136)
12:30:17 [SonarQube analysis]     at org.sonar.java.ast.JavaAstScanner.simpleScan (JavaAstScanner.java:96)
12:30:17 [SonarQube analysis]     at org.sonar.java.ast.JavaAstScanner.scan (JavaAstScanner.java:68)
12:30:17 [SonarQube analysis]     at org.sonar.java.JavaSquid.scanSources (JavaSquid.java:113)

After several hours it just throws Out Of Memory exception

By the way this Foo.java represents pojo with google auto value generations -> com.google.auto.value.AutoValue

Does anybody have some ideas about it ?

Upvotes: 0

Views: 1501

Answers (2)

Wohops
Wohops

Reputation: 3121

The SonarJava plugin for SonarQube contains a few rules relying on a Symbolic Execution (SE) engine, executed during analysis (especially if you are using the SonarWay Quality Profile). From your logs, that's the root of the OOME.

This engine allows some of the SonarJava bug-detection rules to find issues, depending of possible execution path inside a methods (in some circumstances, also following method invocations to other methods).

This engine is heavily resource-consuming. It generates the graph of possible program states (called exploded graph), simulating all the method execution paths according to some constraints. This size of the graph depends of a lot of factors. While the complexity of the method body is one (number of conditions, loops, etc.), another one is the number of parameters, as it represents as many possible starting points for an analysis.

In theory, a new exploded graph is restarted for every file, and the memory freed. While all the rules relying on the SE engine are sharing the same graph to identify issues on a given file, explosion in memory can still occur if the graph is getting surprisingly too big.

So you have a few options:

  • Try to increase the memory allowed to your analysis. Hopefully it will allow the SE engine to cover all the states and end the execution.
  • Exclude from analysis files which may have constructors or methods with a large number of parameters (starting from 15-20, I do expect the engine to start suffering a lot). Typically, it's recommended to avoid analyzing generated code.
  • Ultimately, disable all the rules which are using the SE engine. You will find the rule key in this folder: SonarSource/sonar-java/.../se/checks (look at the @Rule() annotations to get the rule key)

Ideally, if you could isolate the source code reproducing the issue systematically, and extract a self-contained code snippet (compiling only using native java classes and no external dependencies), it would help a lot to identify a potential memory leak in the engine, or define some heuristic to avoid wasting time on methods/constructors the engine won't be able to complete anyway.

Upvotes: 3

Dzmitry Hubin
Dzmitry Hubin

Reputation: 1301

It is funny to admit but when I removed factory method with 35 arguments (yes it was big pojo) it's started to pass analysis successfully. So it can be code specific issue.

Upvotes: 0

Related Questions