Skip to content

Commit ff79c5b

Browse files
committed
SONARJAVA-5695 Report speed of analysis and analysis errors
1 parent 9455861 commit ff79c5b

18 files changed

Lines changed: 283 additions & 67 deletions

File tree

.cirrus.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ sanity_task:
176176
eks_container:
177177
<<: *CONTAINER_WITH_DOCKER_DEFINITION
178178
cpu: 4
179-
memory: 4G
179+
memory: 16G
180180
maven_cache:
181181
folder: ${CIRRUS_WORKING_DIR}/.m2/repository
182182
env:
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class EmptyTest {
2+
}

its/plugin/tests/src/test/java/com/sonar/it/java/JspTest.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import com.sonar.it.java.suite.JavaTestSuite;
2020
import com.sonar.it.java.suite.TestUtils;
21+
import com.sonar.orchestrator.build.BuildResult;
2122
import com.sonar.orchestrator.build.MavenBuild;
2223
import com.sonar.orchestrator.container.Edition;
2324
import com.sonar.orchestrator.junit4.OrchestratorRule;
@@ -30,6 +31,8 @@
3031
import org.junit.ClassRule;
3132
import org.junit.Test;
3233

34+
import static com.sonar.it.java.suite.TestUtils.extractTelemetryLogs;
35+
import static com.sonar.it.java.suite.TestUtils.patternWithLiteralDot;
3336
import static org.assertj.core.api.Assertions.assertThat;
3437

3538
public class JspTest {
@@ -72,7 +75,7 @@ public void should_transpile_jsp() throws Exception {
7275
.setDebugLogs(true)
7376
.setProperty("sonar.scm.disabled", "true");
7477
TestUtils.provisionProject(ENTERPRISE_ORCHESTRATOR_OR_NULL, "org.sonarsource.it.projects:" + PROJECT, PROJECT, "java", "jsp");
75-
ENTERPRISE_ORCHESTRATOR_OR_NULL.executeBuild(build);
78+
BuildResult buildResult = ENTERPRISE_ORCHESTRATOR_OR_NULL.executeBuild(build);
7679

7780
Path visitTest = TestUtils.projectDir(PROJECT).toPath().resolve("target/sonar/visit.txt");
7881
List<String> visitTestLines = Files.readAllLines(visitTest);
@@ -87,5 +90,23 @@ public void should_transpile_jsp() throws Exception {
8790
assertThat(actual).containsExactlyInAnyOrder("index.jsp 1:6",
8891
"include.jsp 3:3",
8992
"test_include.jsp 7:7");
93+
94+
// size of the generated files varies depending on the environment line endings
95+
assertThat(extractTelemetryLogs(buildResult))
96+
.matches(patternWithLiteralDot("""
97+
Telemetry java.analysis.generated.success.size_chars: \\d{5}
98+
Telemetry java.analysis.generated.success.time_ms: \\d+
99+
Telemetry java.analysis.main.success.size_chars: 969
100+
Telemetry java.analysis.main.success.time_ms: \\d+
101+
Telemetry java.analysis.test.success.size_chars: 20
102+
Telemetry java.analysis.test.success.time_ms: \\d+
103+
Telemetry java.dependency.lombok: absent
104+
Telemetry java.dependency.spring-boot: absent
105+
Telemetry java.dependency.spring-web: absent
106+
Telemetry java.is_autoscan: false
107+
Telemetry java.language.version: 8
108+
Telemetry java.module_count: 1
109+
Telemetry java.scanner_app: ScannerMaven
110+
"""));
90111
}
91112
}

its/plugin/tests/src/test/java/com/sonar/it/java/suite/JavaTutorialTest.java

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import org.junit.Test;
2626
import org.sonarqube.ws.Issues.Issue;
2727

28+
import static com.sonar.it.java.suite.TestUtils.extractTelemetryLogs;
29+
import static com.sonar.it.java.suite.TestUtils.patternWithLiteralDot;
2830
import static org.assertj.core.api.Assertions.assertThat;
2931

3032
public class JavaTutorialTest {
@@ -69,12 +71,18 @@ private void executeAndAssertBuild(MavenBuild build, String projectKey) {
6971
assertThat(issuesForRule(issues, "mycompany-java:SecurityAnnotationMandatory")).hasSize(2);
7072
assertThat(issuesForRule(issues, "mycompany-java:SpringControllerRequestMappingEntity")).hasSize(1);
7173

72-
assertThat(buildResult.getLogs())
73-
.containsOnlyOnce("Telemetry java.language.version: 17")
74-
.containsOnlyOnce("Telemetry java.module_count: 1")
75-
.containsOnlyOnce("Telemetry java.scanner_app: ScannerMaven")
76-
.containsOnlyOnce("Telemetry java.dependency.spring-web: 5.3.18")
77-
.containsOnlyOnce("Telemetry java.dependency.lombok: absent");
74+
assertThat(extractTelemetryLogs(buildResult))
75+
.matches(patternWithLiteralDot("""
76+
Telemetry java.analysis.main.success.size_chars: \\d{4}
77+
Telemetry java.analysis.main.success.time_ms: \\d+
78+
Telemetry java.dependency.lombok: absent
79+
Telemetry java.dependency.spring-boot: absent
80+
Telemetry java.dependency.spring-web: 5.3.18
81+
Telemetry java.is_autoscan: false
82+
Telemetry java.language.version: 17
83+
Telemetry java.module_count: 1
84+
Telemetry java.scanner_app: ScannerMaven
85+
"""));
7886
}
7987

8088
private static Stream<String> issuesForRule(List<Issue> issues, String key) {

its/plugin/tests/src/test/java/com/sonar/it/java/suite/TestUtils.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.sonar.it.java.suite;
1818

1919
import com.google.common.collect.Iterables;
20+
import com.sonar.orchestrator.build.BuildResult;
2021
import com.sonar.orchestrator.build.MavenBuild;
2122
import com.sonar.orchestrator.build.SonarScanner;
2223
import com.sonar.orchestrator.container.Server;
@@ -26,6 +27,7 @@
2627
import java.util.Arrays;
2728
import java.util.Collections;
2829
import java.util.List;
30+
import java.util.regex.Pattern;
2931
import org.apache.commons.io.FileUtils;
3032
import org.sonarqube.ws.Issues.Issue;
3133
import org.sonarqube.ws.client.HttpConnector;
@@ -48,6 +50,8 @@ public class TestUtils {
4850
.getParentFile(); // home
4951
}
5052

53+
private static final Pattern TELEMETRY_PATTERN = Pattern.compile("Telemetry java\\.[^\r\n]++");
54+
5155
public static File homeDir() {
5256
return home;
5357
}
@@ -104,4 +108,19 @@ public static MavenBuild createMavenBuild() {
104108
return MavenBuild.create()
105109
.setProperty("sonar.scanner.skipJreProvisioning", "true");
106110
}
111+
112+
113+
public static String extractTelemetryLogs(BuildResult buildResult) {
114+
var telemetryLogs = new StringBuilder();
115+
var telemetryMatcher = TELEMETRY_PATTERN.matcher(buildResult.getLogs());
116+
while (telemetryMatcher.find()) {
117+
telemetryLogs.append(telemetryMatcher.group()).append("\n");
118+
}
119+
return telemetryLogs.toString();
120+
}
121+
122+
public static Pattern patternWithLiteralDot(String regex) {
123+
return Pattern.compile(regex.replace(".", "\\."));
124+
}
125+
107126
}

java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/InternalCheckVerifier.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252
import org.sonar.java.reporting.AnalyzerMessage.TextSpan;
5353
import org.sonar.java.reporting.JavaQuickFix;
5454
import org.sonar.java.reporting.JavaTextEdit;
55+
import org.sonar.java.telemetry.NoOpTelemetry;
56+
import org.sonar.java.telemetry.TelemetryKey;
5557
import org.sonar.java.test.classpath.TestClasspathUtils;
5658
import org.sonar.java.testing.JavaFileScannerContextForTests;
5759
import org.sonar.java.testing.VisitorsBridgeForTests;
@@ -293,7 +295,7 @@ private void verifyAll() {
293295
}
294296
VisitorsBridgeForTests visitorsBridge = visitorsBridgeBuilder.build();
295297

296-
JavaAstScanner astScanner = new JavaAstScanner(sonarComponents);
298+
JavaAstScanner astScanner = new JavaAstScanner(sonarComponents, new NoOpTelemetry(), TelemetryKey.JAVA_ANALYSIS_MAIN);
297299
astScanner.setVisitorBridge(visitorsBridge);
298300

299301
List<InputFile> filesToParse = files;

java-checks-testkit/src/main/java/org/sonar/java/checks/verifier/internal/JavaCheckVerifier.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
import org.sonar.java.model.JavaVersionImpl;
4343
import org.sonar.java.reporting.AnalyzerMessage;
4444
import org.sonar.java.reporting.JavaQuickFix;
45+
import org.sonar.java.telemetry.NoOpTelemetry;
46+
import org.sonar.java.telemetry.TelemetryKey;
4547
import org.sonar.java.test.classpath.TestClasspathUtils;
4648
import org.sonar.java.testing.JavaFileScannerContextForTests;
4749
import org.sonar.java.testing.VisitorsBridgeForTests;
@@ -107,7 +109,7 @@ private MultiFileVerifier createVerifier() {
107109
visitorsBridgeBuilder.enableSemanticWithProjectClasspath(actualClasspath);
108110
}
109111

110-
JavaAstScanner astScanner = new JavaAstScanner(sonarComponents);
112+
JavaAstScanner astScanner = new JavaAstScanner(sonarComponents, new NoOpTelemetry(), TelemetryKey.JAVA_ANALYSIS_MAIN);
111113
VisitorsBridgeForTests visitorsBridge = visitorsBridgeBuilder.build();
112114
astScanner.setVisitorBridge(visitorsBridge);
113115

java-frontend/src/main/java/org/sonar/java/ExecutionTimeReport.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
*/
1717
package org.sonar.java;
1818

19-
import java.io.IOException;
2019
import java.time.Clock;
2120
import java.util.Comparator;
2221
import java.util.LinkedList;
@@ -25,6 +24,7 @@
2524
import org.slf4j.LoggerFactory;
2625
import org.sonar.api.batch.fs.InputFile;
2726
import org.sonar.java.annotations.VisibleForTesting;
27+
import org.sonar.java.model.InputFileUtils;
2828

2929
public class ExecutionTimeReport {
3030
private static final Logger LOG = LoggerFactory.getLogger(ExecutionTimeReport.class);
@@ -81,14 +81,8 @@ public void end() {
8181
LOG.debug("Analysis time of {} ({}ms)", currentFile, currentAnalysisTime);
8282
}
8383
if (currentAnalysisTime >= minRecordedOrderedExecutionTime) {
84-
long currentFileLengthInBytes;
85-
try {
86-
currentFileLengthInBytes = currentFile.contents().length();
87-
} catch (IOException ignored) {
88-
// Ignore and use the default size
89-
currentFileLengthInBytes = -1;
90-
}
91-
recordedOrderedExecutionTime.add(new ExecutionTime(currentFile.toString(), currentAnalysisTime, currentFileLengthInBytes));
84+
long currentFileLength = InputFileUtils.charCount(currentFile, -1);
85+
recordedOrderedExecutionTime.add(new ExecutionTime(currentFile.toString(), currentAnalysisTime, currentFileLength));
9286
recordedOrderedExecutionTime.sort(ORDER_BY_ANALYSIS_TIME_DESCENDING_AND_FILE_ASCENDING);
9387
if (recordedOrderedExecutionTime.size() > MAX_REPORTED_FILES) {
9488
recordedOrderedExecutionTime.removeLast();

java-frontend/src/main/java/org/sonar/java/JavaFrontend.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,15 +108,15 @@ public JavaFrontend(JavaVersion javaVersion, SonarComponents sonarComponents, Me
108108
.flatMap(Collection::stream).distinct().toList();
109109

110110
//AstScanner for main files
111-
astScanner = new JavaAstScanner(sonarComponents);
111+
astScanner = new JavaAstScanner(sonarComponents, telemetry, TelemetryKey.JAVA_ANALYSIS_MAIN);
112112
astScanner.setVisitorBridge(new VisitorsBridge(codeVisitors, classpath, sonarComponents, javaVersion, inAndroidContext));
113113

114114
//AstScanner for test files
115-
astScannerForTests = new JavaAstScanner(sonarComponents);
115+
astScannerForTests = new JavaAstScanner(sonarComponents, telemetry, TelemetryKey.JAVA_ANALYSIS_TEST);
116116
astScannerForTests.setVisitorBridge(new VisitorsBridge(testCodeVisitors, testClasspath, sonarComponents, javaVersion, inAndroidContext));
117117

118118
//AstScanner for generated files
119-
astScannerForGeneratedFiles = new JavaAstScanner(sonarComponents);
119+
astScannerForGeneratedFiles = new JavaAstScanner(sonarComponents, telemetry, TelemetryKey.JAVA_ANALYSIS_GENERATED);
120120
astScannerForGeneratedFiles.setVisitorBridge(new VisitorsBridge(jspCodeVisitors, jspClasspath, sonarComponents, javaVersion, inAndroidContext));
121121
}
122122

java-frontend/src/main/java/org/sonar/java/ast/JavaAstScanner.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,20 @@
3737
import org.sonar.java.AnalysisProgress;
3838
import org.sonar.java.SonarComponents;
3939
import org.sonar.java.annotations.VisibleForTesting;
40+
import org.sonar.java.model.InputFileUtils;
4041
import org.sonar.java.model.JParserConfig;
4142
import org.sonar.java.model.JProblem;
4243
import org.sonar.java.model.JavaTree;
4344
import org.sonar.java.model.VisitorsBridge;
45+
import org.sonar.java.telemetry.NoOpTelemetry;
46+
import org.sonar.java.telemetry.Telemetry;
47+
import org.sonar.java.telemetry.TelemetryKey;
48+
import org.sonar.java.telemetry.TelemetryKey.JavaAnalysisKeys;
4449
import org.sonar.plugins.java.api.JavaVersion;
4550
import org.sonar.plugins.java.api.tree.CompilationUnitTree;
4651

52+
import static java.lang.System.currentTimeMillis;
53+
4754
public class JavaAstScanner {
4855
private static final Logger LOG = LoggerFactory.getLogger(JavaAstScanner.class);
4956

@@ -54,11 +61,15 @@ public class JavaAstScanner {
5461
+ " Such files only exist in Java9+ projects.";
5562

5663
private final SonarComponents sonarComponents;
64+
private final Telemetry telemetry;
65+
private final JavaAnalysisKeys javaAnalysisKeys;
5766
private VisitorsBridge visitor;
5867
private boolean reportedMisconfiguredVersion = false;
5968

60-
public JavaAstScanner(@Nullable SonarComponents sonarComponents) {
69+
public JavaAstScanner(@Nullable SonarComponents sonarComponents, Telemetry telemetry, JavaAnalysisKeys javaAnalysisKeys) {
6170
this.sonarComponents = sonarComponents;
71+
this.telemetry = telemetry;
72+
this.javaAnalysisKeys = javaAnalysisKeys;
6273
}
6374

6475
public List<File> getClasspath() {
@@ -149,20 +160,24 @@ public void simpleScan(InputFile inputFile, JParserConfig.Result result, Consume
149160
// modifyCompilationUnit should be used for testing.
150161
public void simpleScan(InputFile inputFile, JParserConfig.Result result, Consumer<JavaTree.CompilationUnitTreeImpl> cleanUp,
151162
Consumer<CompilationUnitTree> modifyCompilationUnit) {
163+
long startTime = currentTimeMillis();
152164
visitor.setCurrentFile(inputFile);
165+
var telemetryAnalysisKeys = javaAnalysisKeys.exceptions();
153166
try {
154167
JavaTree.CompilationUnitTreeImpl ast = result.get();
155168
modifyCompilationUnit.accept(ast);
156169
visitor.visitFile(ast, sonarComponents != null && sonarComponents.fileCanBeSkipped(inputFile));
157170
String path = inputFile.toString();
158171
collectUndefinedTypes(path, ast.sema.undefinedTypes());
159172
cleanUp.accept(ast);
173+
telemetryAnalysisKeys = javaAnalysisKeys.success();
160174
} catch (RecognitionException e) {
161175
checkInterrupted(e);
162176
LOG.error(String.format(LOG_ERROR_UNABLE_TO_PARSE_FILE, inputFile));
163177
LOG.error(e.getMessage());
164178

165179
parseErrorWalkAndVisit(e, inputFile);
180+
telemetryAnalysisKeys = javaAnalysisKeys.parseErrors();
166181
} catch (AnalysisException e) {
167182
throw e;
168183
} catch (Exception e) {
@@ -171,6 +186,9 @@ public void simpleScan(InputFile inputFile, JParserConfig.Result result, Consume
171186
} catch (StackOverflowError error) {
172187
LOG.error(String.format(LOG_ERROR_STACKOVERFLOW, inputFile), error);
173188
throw error;
189+
} finally {
190+
telemetry.aggregateAsCounter(telemetryAnalysisKeys.sizeCharsKey(), InputFileUtils.charCount(inputFile, 0));
191+
telemetry.aggregateAsCounter(telemetryAnalysisKeys.timeMsKey(), currentTimeMillis() - startTime);
174192
}
175193
}
176194

@@ -237,7 +255,7 @@ public static void scanSingleFileForTests(InputFile file, VisitorsBridge visitor
237255

238256
@VisibleForTesting
239257
public static void scanSingleFileForTests(InputFile inputFile, VisitorsBridge visitorsBridge, @Nullable SonarComponents sonarComponents) {
240-
JavaAstScanner astScanner = new JavaAstScanner(sonarComponents);
258+
JavaAstScanner astScanner = new JavaAstScanner(sonarComponents, new NoOpTelemetry(), TelemetryKey.JAVA_ANALYSIS_MAIN);
241259
astScanner.setVisitorBridge(visitorsBridge);
242260
astScanner.scan(Collections.singleton(inputFile));
243261
}

0 commit comments

Comments
 (0)