Skip to content
Open
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 @@ -1162,7 +1162,7 @@ private static void initializeCrashTracking(boolean delayed, boolean checkNative
SEND_TELEMETRY, "Crashtracking failed to initialize. No additional details available.");
}
} catch (Throwable t) {
log.debug(SEND_TELEMETRY, "Unable to initialize crashtracking", t);
log.debug(SEND_TELEMETRY, "Unable to initialize crashtracking");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public static boolean initialize(boolean forceJmx) {
initializeOOMENotifier(access);
return true;
} catch (Throwable t) {
LOG.debug(SEND_TELEMETRY, "Failed to initialize crash tracking: " + t.getMessage(), t);
LOG.debug("Failed to initialize crash tracking: " + t.getMessage(), t);
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.datadog.profiling.controller.openjdk.events;

import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY;

import datadog.environment.JavaVirtualMachine;
import de.thetaphi.forbiddenapis.SuppressForbidden;
import java.io.BufferedReader;
Expand Down Expand Up @@ -296,7 +294,7 @@ private static Map<Long, String> getAnnotatedRegions() {
}
return annotatedRegions;
} catch (Exception e) {
log.debug(SEND_TELEMETRY, "Failed to get annotated regions", e);
log.debug("Failed to get annotated regions", e);
}
return Collections.emptyMap();
}
Expand All @@ -311,9 +309,9 @@ private void collectEvents(List<SmapEntryEvent> events) {
}
log.debug("Collected {} smap entry events.", events.size());
} catch (IOException e) {
log.debug(SEND_TELEMETRY, "Failed to read smap file", e);
log.debug("Failed to read smap file", e);
} catch (Exception e) {
log.debug(SEND_TELEMETRY, "Failed to parse smap file", e);
log.debug("Failed to parse smap file", e);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package com.datadog.profiling.controller.openjdk.events;

import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY;

import datadog.environment.JavaVirtualMachine;
import datadog.environment.OperatingSystem;
import datadog.trace.api.profiling.ProfilerFlareLogger;
import datadog.trace.bootstrap.instrumentation.jfr.JfrHelper;
import java.lang.management.ManagementFactory;
import java.time.Duration;
Expand Down Expand Up @@ -62,9 +61,8 @@ public static void registerEvents() {
log.debug("Smap entry events registered successfully");
}
} catch (Exception e) {
log.debug(
SEND_TELEMETRY,
"Smap entry events could not be registered due to missing systemMap operation");
ProfilerFlareLogger.getInstance()
.log("Smap entry events could not be registered due to missing systemMap operation", e);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
package com.datadog.profiling.controller;

import datadog.environment.JavaVirtualMachine;
import datadog.environment.OperatingSystem;
import datadog.environment.SystemProperties;
import de.thetaphi.forbiddenapis.SuppressForbidden;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.PosixFilePermissions;
import java.util.Set;
import java.util.function.Supplier;
import java.util.jar.JarFile;

public final class EnvironmentChecker {
private static void appendLine(String line, StringBuilder sb) {
sb.append(line).append(System.lineSeparator());
}

private static void appendLine(Supplier<StringBuilder> sbSupplier) {
sbSupplier.get().append(System.lineSeparator());
}

@SuppressForbidden
public static boolean checkEnvironment(String temp, StringBuilder sb) {
if (!JavaVirtualMachine.isJavaVersionAtLeast(8)) {
appendLine("Profiler requires Java 8 or newer", sb);
return false;
}
appendLine(
() ->
sb.append("Using Java version: ")
.append(JavaVirtualMachine.getRuntimeVersion())
.append(" (")
.append(SystemProperties.getOrDefault("java.home", "unknown"))
.append(")"));
appendLine(
() ->
sb.append("Running as user: ")
.append(SystemProperties.getOrDefault("user.name", "unknown")));
boolean result = false;
result |= checkJFR(sb);
result |= checkDdprof(sb);
if (!result) {;
appendLine("Profiler is not supported on this JVM.", sb);
return false;
} else {
appendLine("Profiler is supported on this JVM.", sb);
}
sb.append(System.lineSeparator());
if (!checkTempLocation(temp, sb)) {
appendLine("Profiler will not work properly due to issues with temp directory location.", sb);
return false;
} else {
if (!temp.equals(SystemProperties.get("java.io.tmpdir"))) {
appendLine(
() ->
sb.append("! Make sure to add '-Ddd.profiling.tempdir=")
.append(temp)
.append("' to your JVM command line !"));
}
}
appendLine("Profiler is ready to be used.", sb);
return true;
}

@SuppressForbidden
private static boolean checkJFR(StringBuilder sb) {
if (JavaVirtualMachine.isOracleJDK8()) {
appendLine(
"JFR is commercial feature in Oracle JDK 8. Make sure you have the right license.", sb);
return true;
} else if (JavaVirtualMachine.isJ9()) {
appendLine("JFR is not supported on J9 JVM.", sb);
return false;
} else {
appendLine(
() -> sb.append("JFR is supported on ").append(JavaVirtualMachine.getRuntimeVersion()));
return true;
}
}

@SuppressForbidden
private static boolean checkDdprof(StringBuilder sb) {
if (!OperatingSystem.isLinux()) {
appendLine("Datadog profiler is only supported on Linux.", sb);
return false;
} else {
appendLine(
() ->
sb.append("Datadog profiler is supported on ")
.append(JavaVirtualMachine.getRuntimeVersion()));
return true;
}
}

@SuppressForbidden
private static boolean checkTempLocation(String temp, StringBuilder sb) {
// Check if the temp directory is writable
if (temp == null || temp.isEmpty()) {
appendLine("Temp directory is not specified.", sb);
return false;
}

appendLine(() -> sb.append("Checking temporary directory: ").append(temp));

Path base = Paths.get(temp);
if (!Files.exists(base)) {
appendLine(() -> sb.append("Temporary directory does not exist: ").append(base));
return false;
}
Path target = base.resolve("dd-profiler").normalize();
boolean rslt = true;
Set<String> supportedViews = FileSystems.getDefault().supportedFileAttributeViews();
boolean isPosix = supportedViews.contains("posix");
try {
if (isPosix) {
Files.createDirectories(
target,
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------")));
} else {
// non-posix, eg. Windows - let's rely on the created folders being world-writable
Files.createDirectories(target);
}
appendLine(() -> sb.append("Temporary directory is writable: ").append(target));
rslt &= checkCreateTempFile(target, sb);
rslt &= checkLoadLibrary(target, sb);
} catch (Exception e) {
appendLine(() -> sb.append("Unable to create temp directory in location ").append(temp));
if (isPosix) {
appendLine(
() ->
sb.append("Base dir: ")
.append(base)
.append(" [")
.append(getPermissionsStringSafe(base))
.append("]"));
}
appendLine(() -> sb.append("Error: ").append(e));
} finally {
if (Files.exists(target)) {
try {
Files.walkFileTree(
target,
new FileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFileFailed(Path file, IOException exc)
throws IOException {
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException ignored) {
// should never happen
}
}
}
return rslt;
}

private static String getPermissionsStringSafe(Path file) {
try {
return PosixFilePermissions.toString(Files.getPosixFilePermissions(file));
} catch (IOException ignored) {
return "<unavailable>";
}
}

@SuppressForbidden
private static boolean checkCreateTempFile(Path target, StringBuilder sb) {
// create a file to check if the directory is writable
try {
appendLine(() -> sb.append("Attempting to create a test file in: ").append(target));
Path testFile = target.resolve("testfile");
Files.createFile(testFile);
appendLine(() -> sb.append("Test file created: ").append(testFile));
return true;
} catch (Exception e) {
appendLine(() -> sb.append("Unable to create test file in temp directory ").append(target));
appendLine(() -> sb.append("Error: ").append(e));
}
return false;
}

@SuppressForbidden
private static boolean checkLoadLibrary(Path target, StringBuilder sb) {
if (!OperatingSystem.isLinux()) {
// we are loading the native library only on linux
appendLine("Skipping native library check on non-linux platform", sb);
return true;
}
boolean rslt = true;
try {
rslt &= extractSoFromJar(target, sb);
if (rslt) {
Path libFile = target.resolve("libjavaProfiler.so");
appendLine(() -> sb.append("Attempting to load native library from: ").append(libFile));
System.load(libFile.toString());
appendLine("Native library loaded successfully", sb);
}
return true;
} catch (Throwable t) {
appendLine(
() -> sb.append("Unable to load native library in temp directory ").append(target));
appendLine(() -> sb.append("Error: ").append(t));
return false;
}
}

@SuppressForbidden
private static boolean extractSoFromJar(Path target, StringBuilder sb) throws Exception {
URL jarUrl = EnvironmentChecker.class.getProtectionDomain().getCodeSource().getLocation();
try (JarFile jarFile = new JarFile(new File(jarUrl.toURI()))) {
return jarFile.stream()
.filter(e -> e.getName().contains("libjavaProfiler.so"))
.filter(
e ->
e.getName()
.contains(OperatingSystem.isAarch64() ? "/linux-arm64/" : "/linux-x64/")
&& (!OperatingSystem.isMusl() || e.getName().contains("-musl")))
.findFirst()
.map(
e -> {
try {
Path soFile = target.resolve("libjavaProfiler.so");
Files.createDirectories(soFile.getParent());
Files.copy(jarFile.getInputStream(e), soFile);
appendLine(() -> sb.append("Native library extracted to: ").append(soFile));
return true;
} catch (Throwable t) {
appendLine("Failed to extract or load native library", sb);
appendLine(() -> sb.append("Error: ").append(t));
}
return false;
})
.orElse(Boolean.FALSE);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.datadog.profiling.agent;
package com.datadog.profiling.controller;

import com.datadog.profiling.controller.ProfilingSupport;
import datadog.trace.api.Config;
import datadog.trace.api.config.ProfilingConfig;
import datadog.trace.api.flare.TracerFlare;
Expand All @@ -15,14 +14,14 @@

public final class ProfilerFlareReporter implements TracerFlare.Reporter {
private static final ProfilerFlareReporter INSTANCE = new ProfilerFlareReporter();
private static Exception profilerInitializationException;
private volatile Exception profilerInitializationException;

public static void register() {
TracerFlare.addReporter(INSTANCE);
}

public static void reportInitializationException(Exception e) {
profilerInitializationException = e;
INSTANCE.profilerInitializationException = e;
}

@Override
Expand All @@ -40,6 +39,12 @@ public void addReportToFlare(ZipOutputStream zip) throws IOException {
// no-op, ignore if we can't read the template override file
}
}

StringBuilder envCheck = new StringBuilder();
String tempDir = ConfigProvider.getInstance().getString(ProfilingConfig.PROFILING_TEMP_DIR);
EnvironmentChecker.checkEnvironment(
tempDir != null ? tempDir : System.getProperty("java.io.tmpdir"), envCheck);
TracerFlare.addText(zip, "profiler_env.txt", envCheck.toString());
}

private String getProfilerConfig() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,6 @@ protected ProfilerSettingsSupport(
// usually set via DD_INSTRUMENTATION_INSTALL_TYPE env var
configProvider.getString("instrumentation.install.type");
this.profilerActivationSetting = getProfilerActivation(configProvider);

logger.debug(
SEND_TELEMETRY,
"Profiler settings: " + this); // telemetry receiver does not recognize formatting
}

private static int getStackDepth() {
Expand All @@ -212,8 +208,7 @@ private static int getStackDepth() {
try {
return Integer.parseInt(value.substring(start, end));
} catch (NumberFormatException e) {
logger.debug(
SEND_TELEMETRY, "Failed to parse stack depth from JFR options: {}", value, e);
logger.debug(SEND_TELEMETRY, "Failed to parse stack depth from JFR options");
}
}
}
Expand Down
Loading