8000 WebSocket support for Netty by ValentinZakharov · Pull Request #8632 · DataDog/dd-trace-java · GitHub
[go: up one dir, main page]

Skip to content

WebSocket support for Netty #8632

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

Merged
merged 36 commits into from
May 13, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e619f2a
Implemented WebSocket support for Netty 4.1
ValentinZakharov Mar 28, 2025
1ab91db
Let propagate unhandled events and fix tests
amarziali Mar 28, 2025
7f96ea2
Refactoring
ValentinZakharov Mar 30, 2025
b2d45a7
Refactor netty test and fix instrumentation
amarziali Apr 3, 2025
0d7330c
Improved pipeline processing - now you can insert handler in any place
ValentinZakharov Apr 3, 2025
7f1138f
Merge branch 'master' into vzakharov/websockets_netty
ValentinZakharov Apr 4, 2025
ee2c5fd
Merge branch 'master' into vzakharov/websockets_netty
ValentinZakharov Apr 7, 2025
203e468
Fixed helper
ValentinZakharov Apr 8, 2025
d753e90
Refactoring
ValentinZakharov Apr 8, 2025
af33164
WebSocket Server support for netty-4.0
ValentinZakharov Apr 9, 2025
e79c661
Missing handlers use cases for netty-4.1
ValentinZakharov Apr 10, 2025
19417b9
Fixed handlers for netty-4.0
ValentinZakharov Apr 10, 2025
6e94f0f
Tests for netty-4.0
ValentinZakharov Apr 10, 2025
c9a7c20
Refactoring
ValentinZakharov Apr 10, 2025
86715ec
WebSocket Server support for netty-3.8
ValentinZakharov Apr 11, 2025
dabfe2d
Tests for netty-3.8
ValentinZakharov Apr 15, 2025
e7f4d7f
Spotless
ValentinZakharov Apr 15, 2025
5fd4cf2
8000 Fixed tests
ValentinZakharov Apr 16, 2025
091ee80
Add profiler env check command to AgentCLI (#8671)
jbachorik Apr 7, 2025
eb18875
Remove dependency on bash from crash/oome uploder scripts (#8652)
jbachorik Apr 7, 2025
66f7b9f
Do not apply JUnit 4 instrumentation to MUnit runners (#8675)
nikita-tkachenko-datadog Apr 7, 2025
d851aa9
Shutdown CI Visibility test event handlers before tracer (#8677)
nikita-tkachenko-datadog Apr 7, 2025
c7ec7ce
Prevent double reporting of Scalatest events when using SBT with test…
nikita-tkachenko-datadog Apr 8, 2025
ba3f346
Fix In-Product when config is empty (#8679)
jpbempel Apr 9, 2025
2203ec4
Expand MUnit runners filter to catch munit.MUnitRunner in JUnit 4 ins…
daniel-mohedano Apr 9, 2025
fef00c7
Remove unused TestEventsHandler methods (#8674)
nikita-tkachenko-datadog Apr 9, 2025
15889fc
Delete print line (#8686)
sarahchen6 Apr 9, 2025
b838ed1
Exclude ProxyLeakTask exception from exception profiling (#8666)
jbachorik Apr 10, 2025
690dd95
Use jvmstat for JDKs 9+ programmatically (#8641)
MattAlp Apr 10, 2025
6510fa4
Update test.retry_reason to use full name of the feature (#8689)
daniel-mohedano Apr 10, 2025
b8b8dab
Allow dogstatsd port to be configurable with DD_DOGSTATSD_PORT (#8693)
randomanderson Apr 14, 2025
5922b70
wait the client handshake
amarziali Apr 17, 2025
78e9c9c
move netty ws client to interested modules
amarziali Apr 17, 2025
bc4b6f0
Merge remote-tracking branch 'origin/master' into vzakharov/websocket…
amarziali Apr 17, 2025
97e7d2f
Added WebSocket tracing check
ValentinZakharov Apr 28, 2025
c817fc9
Merge branch 'master' into vzakharov/websockets_netty
ValentinZakharov Apr 28, 2025
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
Prev Previous commit
Next Next commit
Add profiler env check command to AgentCLI (#8671)
  • Loading branch information
jbachorik authored and amarziali committed Apr 17, 2025
commit 091ee8019383de0e9739cddc364b35016a87d85f
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ static void initialize(String onOutOfMemoryVal) {
Path scriptPath = getOOMEScripPath(onOutOfMemoryVal);
if (scriptPath == null) {
LOG.debug(
"OOME notifier script value ({}) does not follow the expected format: <path>/dd_ome_notifier.(sh|bat) %p. OOME tracking is disabled.",
"OOME notifier script value ({}) does not follow the expected format: <path>/dd_oome_notifier.(sh|bat) %p. OOME tracking is disabled.",
onOutOfMemoryVal);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.datadog.crashtracking.OOMENotifier;
import datadog.trace.agent.tooling.bytebuddy.SharedTypePools;
import datadog.trace.agent.tooling.bytebuddy.matcher.HierarchyMatchers;
import datadog.trace.agent.tooling.profiler.EnvironmentChecker;
import datadog.trace.bootstrap.Agent;
import datadog.trace.bootstrap.InitializationTelemetry;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
Expand Down Expand Up @@ -126,6 +127,12 @@ public static void scanDependencies(final String[] args) throws Exception {
System.out.println("Scan finished");
}

public static void checkProfilerEnv(String temp) {
if (!EnvironmentChecker.checkEnvironment(temp)) {
System.exit(1);
}
}

private static void recursiveDependencySearch(Consumer<File> invoker, File origin)
throws IOException {
invoker.accept(origin);
Expand Down
8000 628C
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package datadog.trace.agent.tooling.profiler;

import datadog.trace.api.Platform;
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.jar.JarFile;

public final class EnvironmentChecker {
public static boolean checkEnvironment(String temp) {
if (!Platform.isJavaVersionAtLeast(8)) {
System.out.println("Profiler requires Java 8 or newer");
return false;
}
System.out.println(
"Using Java version: "
+ Platform.getRuntimeVersion()
+ " ("
+ System.getProperty("java.home")
+ ")");
System.out.println("Running as user: " + System.getProperty("user.name"));
boolean result = false;
result |= checkJFR();
result |= checkDdprof();
if (!result) {;
System.out.println("Profiler is not supported on this JVM.");
return false;
} else {
System.out.println("Profiler is supported on this JVM.");
}
System.out.println();
if (!checkTempLocation(temp)) {
System.out.println(
"Profiler will not work properly due to issues with temp directory location.");
return false;
} else {
if (!temp.equals(System.getProperty("java.io.tmpdir"))) {
System.out.println(
"! Make sure to add '-Ddd.profiling.tempdir=" + temp + "' to your JVM command line !");
}
}
System.out.println("Profiler is ready to be used.");
return true;
}

private static boolean checkJFR() {
if (Platform.isOracleJDK8()) {
System.out.println(
"JFR is commercial feature in Oracle JDK 8. Make sure you have the right license.");
return true;
} else if (Platform.isJ9()) {
System.out.println("JFR is not supported on J9 JVM.");
return false;
} else {
System.out.println("JFR is supported on " + Platform.getRuntimeVersion());
return true;
}
}

private static boolean checkDdprof() {
if (!Platform.isLinux()) {
System.out.println("Datadog profiler is only supported on Linux.");
return false;
} else {
System.out.println("Datadog profiler is supported on " + Platform.getRuntimeVersion());
return true;
}
}

private static boolean checkTempLocation(String temp) {
// Check if the temp directory is writable
if (temp == null || temp.isEmpty()) {
System.out.println("Temp directory is not specified.");
return false;
}

System.out.println("Checking temporary directory: " + temp);

Path base = Paths.get(temp);
if (!Files.exists(base)) {
System.out.println("Temporary directory does not exist: " + 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);
}
System.out.println("Temporary directory is writable: " + target);
rslt &= checkCreateTempFile(target);
rslt &= checkLoadLibrary(target);
} catch (Exception e) {
System.out.println("Unable to create temp directory in location " + temp);
if (isPosix) {
try {
System.out.println(
"Base dir: "
+ base
+ " ["
+ PosixFilePermissions.toString(Files.getPosixFilePermissions(base))
+ "]");
} catch (IOException ignored) {
// never happens
}
}
System.out.println("Error: " + 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 boolean checkCreateTempFile(Path target) {
// create a file to check if the directory is writable
try {
System.out.println("Attempting to create a test file in: " + target);
Path testFile = target.resolve("testfile");
Files.createFile(testFile);
System.out.println("Test file created: " + testFile);
return true;
} catch (Exception e) {
System.out.println("Unable to create test file in temp directory " + target);
System.out.println("Error: " + e);
}
return false;
}

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

private static boolean extractSoFromJar(Path target) 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(Platform.isAarch64() ? "/linux-arm64/" : "/linux-x64/")
&& (!Platform.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);
System.out.println("Native library extracted to: " + soFile);
return true;
} catch (Throwable t) {
System.out.println("Failed to extract or load native library");
System.out.println("Error: " + t);
}
return false;
})
.orElse(Boolean.FALSE);
}
}
}
10 changes: 10 additions & 0 deletions dd-java-agent/src/main/java/datadog/trace/bootstrap/AgentJar.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ public static void main(final String[] args) {
case "scanDependencies":
scanDependencies(args);
break;
case "checkProfilerEnv":
checkProfilerEnv(args);
break;
case "--list-integrations":
case "-li":
printIntegrationNames();
Expand Down Expand Up @@ -63,6 +66,7 @@ private static void printUsage() {
System.out.println(" sampleTrace [-c count] [-i interval]");
System.out.println(" uploadCrash file ...");
System.out.println(" scanDependencies <path> ...");
System.out.println(" checkProfilerEnv [temp]");
System.out.println(" [-li | --list-integrations]");
System.out.println(" [-h | --help]");
System.out.println(" [-v | --version]");
Expand Down Expand Up @@ -164,4 +168,10 @@ public static String getAgentVersion() throws IOException {

return sb.toString().trim();
}

private static void checkProfilerEnv(final String[] args) throws Exception {
String tmpDir = args.length == 2 ? args[1] : System.getProperty("java.io.tmpdir");

installAgentCLI().getMethod("checkProfilerEnv", String.class).invoke(null, tmpDir);
}
}
96 changes: 96 additions & 0 deletions internal-api/src/main/java/datadog/trace/api/Platform.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
package datadog.trace.api;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;

Expand Down Expand Up @@ -276,6 +284,94 @@ public static boolean isMac() {
return os.contains("mac");
}

public static boolean isAarch64() {
return System.getProperty("os.arch").toLowerCase().contains("aarch64");
}

public static boolean isMusl() {
if (!isLinux()) {
return false;
}
// check the Java exe then fall back to proc/self maps
try {
return isMuslJavaExecutable();
} catch (IOException e) {
try {
return isMuslProcSelfMaps();
} catch (IOException ignore) {
return false;
}
}
}

static boolean isMuslProcSelfMaps() throws IOException {
try (BufferedReader reader = new BufferedReader(new FileReader("/proc/self/maps"))) {
String line;
while ((line = reader.readLine()) != null) {
if (line.contains("-musl-")) {
return true;
}
if (line.contains("/libc.")) {
return false;
}
}
}
return false;
}

/**
* There is information about the linking in the ELF file. Since properly parsing ELF is not
* trivial this code will attempt a brute-force approach and will scan the first 4096 bytes of the
* 'java' program image for anything prefixed with `/ld-` - in practice this will contain
* `/ld-musl` for musl systems and probably something else for non-musl systems (eg.
* `/ld-linux-...`). However, if such string is missing should indicate that the system is not a
* musl one.
*/
static boolean isMuslJavaExecutable() throws IOException {

byte[] magic = new byte[] {(byte) 0x7f, (byte) 'E', (byte) 'L', (byte) 'F'};
byte[] prefix = new byte[] {(byte) '/', (byte) 'l', (byte) 'd', (byte) '-'}; // '/ld-*'
byte[] musl = new byte[] {(byte) 'm', (byte) 'u', (byte) 's', (byte) 'l'}; // 'musl'

Path binary = Paths.get(System.getProperty("java.home"), "bin", "java");
byte[] buffer = new byte[4096];

try (InputStream is = Files.newInputStream(binary)) {
int read = is.read(buffer, 0, 4);
if (read != 4 || !containsArray(buffer, 0, magic)) {
throw new IOException(Arrays.toString(buffer));
}
read = is.read(buffer);
if (read <= 0) {
throw new IOException();
}
int prefixPos = 0;
for (int i = 0; i < read; i++) {
if (buffer[i] == prefix[prefixPos]) {
if (++prefixPos == prefix.length) {
return containsArray(buffer, i + 1, musl);
}
} else {
prefixPos = 0;
}
}
}
return false;
}

private static boolean containsArray(byte[] container, int offset, byte[] contained) {
for (int i = 0; i < contained.length; i++) {
int leftPos = offset + i;
if (leftPos >= container.length) {
return false;
}
if (container[leftPos] != contained[i]) {
return false;
}
}
return true;
}

public static boolean isOracleJDK8() {
return isJavaVersion(8)
&& RUNTIME.vendor.contains("Oracle")
Expand Down
0