From a41d1512de940202580a6511823fd70f2681753f Mon Sep 17 00:00:00 2001 From: Jean-Philippe Bempel Date: Wed, 29 Jan 2025 15:42:27 +0100 Subject: [PATCH] Add SymDB report for any jar scanning failures (#8300) Any bail out when resolving or scanning for jars will be reported into SymDBReport and logged as a single INFO log line at the end of SYMDB extraction process from SymDB enablement --- .../datadog/debugger/symbol/JarScanner.java | 10 ++-- .../debugger/symbol/SymDBEnablement.java | 11 +++-- .../datadog/debugger/symbol/SymDBReport.java | 49 +++++++++++++++++++ .../debugger/symbol/SymbolAggregator.java | 2 +- .../debugger/symbol/JarScannerTest.java | 9 ++-- 5 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBReport.java diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/JarScanner.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/JarScanner.java index ea3c48a73a4..2f106413931 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/JarScanner.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/JarScanner.java @@ -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; } @@ -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; } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBEnablement.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBEnablement.java index 17fd25f808a..c7bb2ee41f6 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBEnablement.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBEnablement.java @@ -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) @@ -130,7 +132,7 @@ public void startSymbolExtraction() { } } - private void extractSymbolForLoadedClasses() { + private void extractSymbolForLoadedClasses(SymDBReport symDBReport) { Class[] classesToExtract; try { classesToExtract = @@ -148,7 +150,7 @@ 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); } @@ -156,11 +158,13 @@ private void extractSymbolForLoadedClasses() { 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())) { @@ -178,6 +182,7 @@ private void extractSymbolForLoadedClasses() { } alreadyScannedJars.add(jarPath.toString()); } catch (IOException e) { + symDBReport.addIOException(jarPath.toString(), e); throw new RuntimeException(e); } } diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBReport.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBReport.java new file mode 100644 index 00000000000..af450f42ccf --- /dev/null +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymDBReport.java @@ -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 missingJars = new HashSet<>(); + private final Set directoryJars = new HashSet<>(); + private final Map ioExceptions = new HashMap<>(); + private final List 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); + } +} diff --git a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymbolAggregator.java b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymbolAggregator.java index f956fe90851..a7e7005520e 100644 --- a/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymbolAggregator.java +++ b/dd-java-agent/agent-debugger/src/main/java/com/datadog/debugger/symbol/SymbolAggregator.java @@ -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(); diff --git a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/JarScannerTest.java b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/JarScannerTest.java index 8d2136adcd2..54f84f99499 100644 --- a/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/JarScannerTest.java +++ b/dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/symbol/JarScannerTest.java @@ -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 @@ -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 @@ -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()); } }