Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SymDB report for any jar scanning failures #8300

Merged
merged 1 commit into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ public class JarScanner {
private static final String SPRING_CLASSES_PREFIX = "BOOT-INF/classes/";
private static final String SPRING_DEPS_PREFIX = "BOOT-INF/lib/";

public static Path extractJarPath(Class<?> clazz) throws URISyntaxException {
return extractJarPath(clazz.getProtectionDomain());
public static Path extractJarPath(Class<?> clazz, SymDBReport symDBReport)
throws URISyntaxException {
return extractJarPath(clazz.getProtectionDomain(), symDBReport);
}

public static Path extractJarPath(ProtectionDomain protectionDomain) throws URISyntaxException {
public static Path extractJarPath(ProtectionDomain protectionDomain, SymDBReport symDBReport) {
if (protectionDomain == null) {
return null;
}
Expand All @@ -50,6 +51,9 @@ public static Path extractJarPath(ProtectionDomain protectionDomain) throws URIS
} else if (locationStr.startsWith(FILE_PREFIX)) {
return getPathFromPrefixedFileName(locationStr, FILE_PREFIX, locationStr.length());
}
if (symDBReport != null) {
symDBReport.addLocationError(locationStr);
}
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,9 @@ public void startSymbolExtraction() {
symbolExtractionTransformer =
new SymbolExtractionTransformer(symbolAggregator, classNameFilter);
instrumentation.addTransformer(symbolExtractionTransformer);
extractSymbolForLoadedClasses();
SymDBReport symDBReport = new SymDBReport();
extractSymbolForLoadedClasses(symDBReport);
symDBReport.report();
lastUploadTimestamp = System.currentTimeMillis();
} catch (Throwable ex) {
// catch all Throwables because LinkageError is possible (duplicate class definition)
Expand All @@ -130,7 +132,7 @@ public void startSymbolExtraction() {
}
}

private void extractSymbolForLoadedClasses() {
private void extractSymbolForLoadedClasses(SymDBReport symDBReport) {
Class<?>[] classesToExtract;
try {
classesToExtract =
Expand All @@ -148,19 +150,21 @@ private void extractSymbolForLoadedClasses() {
for (Class<?> clazz : classesToExtract) {
Path jarPath;
try {
jarPath = JarScanner.extractJarPath(clazz);
jarPath = JarScanner.extractJarPath(clazz, symDBReport);
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
if (jarPath == null) {
continue;
}
if (!Files.exists(jarPath)) {
symDBReport.addMissingJar(jarPath.toString());
continue;
}
File jarPathFile = jarPath.toFile();
if (jarPathFile.isDirectory()) {
// we are not supporting class directories (classpath) but only jar files
symDBReport.addDirectoryJar(jarPath.toString());
continue;
}
if (alreadyScannedJars.contains(jarPath.toString())) {
Expand All @@ -178,6 +182,7 @@ private void extractSymbolForLoadedClasses() {
}
alreadyScannedJars.add(jarPath.toString());
} catch (IOException e) {
symDBReport.addIOException(jarPath.toString(), e);
throw new RuntimeException(e);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.datadog.debugger.symbol;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class SymDBReport {
private static final Logger LOGGER = LoggerFactory.getLogger(SymDBReport.class);

private final Set<String> missingJars = new HashSet<>();
private final Set<String> directoryJars = new HashSet<>();
private final Map<String, String> ioExceptions = new HashMap<>();
private final List<String> locationErrors = new ArrayList<>();

public void addMissingJar(String jarPath) {
missingJars.add(jarPath);
}

public void addDirectoryJar(String jarPath) {
directoryJars.add(jarPath);
}

public void addIOException(String jarPath, IOException e) {
ioExceptions.put(jarPath, e.toString());
}

public void addLocationError(String locationStr) {
locationErrors.add(locationStr);
}

public void report() {
String content =
"== SymDB Report == Location errors:"
+ locationErrors
+ " Missing jars: "
+ missingJars
+ " Directory jars: "
+ directoryJars
+ " IOExceptions: "
+ ioExceptions;
LOGGER.info(content);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mhmm, this just goes to the user logs right? Would it make sense to rather submit it to instrumentation telemetry? I don't know if we are doing any of that at the moment. Happy to have it merged in user logs, just wondering.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am scared about the volume. This log will be sent for any restart of a service

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public void parseClass(
String className, byte[] classfileBuffer, ProtectionDomain protectionDomain) {
try {
String jarName = "DEFAULT";
Path jarPath = JarScanner.extractJarPath(protectionDomain);
Path jarPath = JarScanner.extractJarPath(protectionDomain, null);
if (jarPath != null && Files.exists(jarPath)) {
LOGGER.debug("jarpath: {}", jarPath);
jarName = jarPath.toString();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ public void extractJarPathFromJar()
URL jarUrl = new URL("jar:file:" + jarFileUrl.getFile() + "!/");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[] {jarUrl}, null);
Class<?> testClass = urlClassLoader.loadClass(CLASS_NAME);
assertEquals(jarFileUrl.getFile(), JarScanner.extractJarPath(testClass).toString());
assertEquals(jarFileUrl.getFile(), JarScanner.extractJarPath(testClass, null).toString());
assertEquals(
jarFileUrl.getFile(),
JarScanner.extractJarPath(testClass.getProtectionDomain()).toString());
JarScanner.extractJarPath(testClass.getProtectionDomain(), null).toString());
}

@Test
Expand All @@ -34,7 +34,7 @@ public void extractJarPathFromFile() throws ClassNotFoundException, URISyntaxExc
URL jarFileUrl = getClass().getResource("/debugger-symbol.jar");
URLClassLoader urlClassLoader = new URLClassLoader(new URL[] {jarFileUrl}, null);
Class<?> testClass = urlClassLoader.loadClass(CLASS_NAME);
assertEquals(jarFileUrl.getFile(), JarScanner.extractJarPath(testClass).toString());
assertEquals(jarFileUrl.getFile(), JarScanner.extractJarPath(testClass, null).toString());
}

@Test
Expand All @@ -45,6 +45,7 @@ public void extractJarPathFromNestedJar() throws URISyntaxException {
.thenReturn("jar:nested:" + jarFileUrl.getFile() + "/!BOOT-INF/classes/!");
CodeSource codeSource = new CodeSource(mockLocation, (Certificate[]) null);
ProtectionDomain protectionDomain = new ProtectionDomain(codeSource, null);
assertEquals(jarFileUrl.getFile(), JarScanner.extractJarPath(protectionDomain).toString());
assertEquals(
jarFileUrl.getFile(), JarScanner.extractJarPath(protectionDomain, null).toString());
}
}
Loading