Skip to content

Commit d6b3f0e

Browse files
Merge branch 'master' into alexeyk/update-jackson
2 parents b535081 + 1052f49 commit d6b3f0e

20 files changed

Lines changed: 860 additions & 343 deletions

File tree

.github/workflows/run-system-tests.yaml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@ name: Run system tests
33
on:
44
pull_request:
55
workflow_dispatch:
6+
schedule:
7+
- cron: 0 4 * * *
8+
push:
9+
branches:
10+
- master
611

712
# Cancel long-running jobs when a new commit is pushed
813
concurrency:
9-
group: ${{ github.workflow }}-${{ github.ref }}
14+
# this ensures that only one workflow runs at a time for a given branch on pull requests
15+
# as the group key is the workflow name and the branch name
16+
# for scheduled runs and pushes to master, we use the run id to ensure that all runs are executed
17+
group: ${{ (github.event_name == 'pull_request' && format('{0}-{1}', github.workflow, github.ref)) || format('{0}-{1}', github.workflow, github.run_id) }}
1018
cancel-in-progress: true
1119

1220
jobs:

.gitlab-ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ variables:
2828
GRADLE_VERSION: "8.14.3" # must match gradle-wrapper.properties
2929
MAVEN_REPOSITORY_PROXY: "https://depot-read-api-java.us1.ddbuild.io/magicmirror/magicmirror/@current/"
3030
GRADLE_PLUGIN_PROXY: "https://depot-read-api-java.us1.ddbuild.io/magicmirror/magicmirror/@current/"
31-
BUILDER_IMAGE_VERSION_PREFIX: "v25.07-" # use either an empty string (e.g. "") for latest images or a version followed by a hyphen (e.g. "v25.05-")
31+
BUILDER_IMAGE_VERSION_PREFIX: "v25.09-" # use either an empty string (e.g. "") for latest images or a version followed by a hyphen (e.g. "v25.05-")
3232
REPO_NOTIFICATION_CHANNEL: "#apm-java-escalations"
3333
DEFAULT_TEST_JVMS: /^(8|11|17|21|25)$/ # the latest "stable" version is LTS v25
3434
PROFILE_TESTS:

dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/Agent.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1162,7 +1162,7 @@ private static void initializeCrashTracking(boolean delayed, boolean checkNative
11621162
SEND_TELEMETRY, "Crashtracking failed to initialize. No additional details available.");
11631163
}
11641164
} catch (Throwable t) {
1165-
log.debug(SEND_TELEMETRY, "Unable to initialize crashtracking", t);
1165+
log.debug(SEND_TELEMETRY, "Unable to initialize crashtracking");
11661166
}
11671167
}
11681168

dd-java-agent/agent-crashtracking/src/main/java/datadog/crashtracking/Initializer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public static boolean initialize(boolean forceJmx) {
103103
initializeOOMENotifier(access);
104104
return true;
105105
} catch (Throwable t) {
106-
LOG.debug(SEND_TELEMETRY, "Failed to initialize crash tracking: " + t.getMessage(), t);
106+
LOG.debug("Failed to initialize crash tracking: {}", t.getMessage(), t);
107107
}
108108
return false;
109109
}

dd-java-agent/agent-debugger/src/test/java/com/datadog/debugger/agent/CapturedSnapshotTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,7 @@ public void sourceFileProbeGroovy() throws IOException, URISyntaxException {
621621
}
622622

623623
@Test
624+
@EnabledForJreRange(max = JRE.JAVA_24)
624625
@DisabledIf(
625626
value = "datadog.environment.JavaVirtualMachine#isJ9",
626627
disabledReason = "Issue with J9 when compiling Kotlin code")
@@ -651,6 +652,7 @@ public void sourceFileProbeKotlin() throws IOException, URISyntaxException {
651652
}
652653

653654
@Test
655+
@EnabledForJreRange(max = JRE.JAVA_24)
654656
@DisabledIf(
655657
value = "datadog.environment.JavaVirtualMachine#isJ9",
656658
disabledReason = "Issue with J9 when compiling Kotlin code")
@@ -678,6 +680,7 @@ public void suspendKotlin() throws IOException, URISyntaxException {
678680
}
679681

680682
@Test
683+
@EnabledForJreRange(max = JRE.JAVA_24)
681684
@DisabledIf(
682685
value = "datadog.environment.JavaVirtualMachine#isJ9",
683686
disabledReason = "Issue with J9 when compiling Kotlin code")
@@ -711,6 +714,7 @@ public void suspendMethodKotlin() {
711714
}
712715

713716
@Test
717+
@EnabledForJreRange(max = JRE.JAVA_24)
714718
@DisabledIf(
715719
value = "datadog.environment.JavaVirtualMachine#isJ9",
716720
disabledReason = "Issue with J9 when compiling Kotlin code")

dd-java-agent/agent-profiling/profiling-controller-openjdk/src/main/java/com/datadog/profiling/controller/openjdk/events/SmapEntryCache.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
package com.datadog.profiling.controller.openjdk.events;
22

3-
import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY;
4-
53
import datadog.environment.JavaVirtualMachine;
64
import de.thetaphi.forbiddenapis.SuppressForbidden;
75
import java.io.BufferedReader;
@@ -296,7 +294,7 @@ private static Map<Long, String> getAnnotatedRegions() {
296294
}
297295
return annotatedRegions;
298296
} catch (Exception e) {
299-
log.debug(SEND_TELEMETRY, "Failed to get annotated regions", e);
297+
log.debug("Failed to get annotated regions", e);
300298
}
301299
return Collections.emptyMap();
302300
}
@@ -311,9 +309,9 @@ private void collectEvents(List<SmapEntryEvent> events) {
311309
}
312310
log.debug("Collected {} smap entry events.", events.size());
313311
} catch (IOException e) {
314-
log.debug(SEND_TELEMETRY, "Failed to read smap file", e);
312+
log.debug("Failed to read smap file", e);
315313
} catch (Exception e) {
316-
log.debug(SEND_TELEMETRY, "Failed to parse smap file", e);
314+
log.debug("Failed to parse smap file", e);
317315
}
318316
}
319317
}

dd-java-agent/agent-profiling/profiling-controller-openjdk/src/main/java/com/datadog/profiling/controller/openjdk/events/SmapEntryFactory.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
package com.datadog.profiling.controller.openjdk.events;
22

3-
import static datadog.trace.api.telemetry.LogCollector.SEND_TELEMETRY;
4-
53
import datadog.environment.JavaVirtualMachine;
64
import datadog.environment.OperatingSystem;
5+
import datadog.trace.api.profiling.ProfilerFlareLogger;
76
import datadog.trace.bootstrap.instrumentation.jfr.JfrHelper;
87
import java.lang.management.ManagementFactory;
98
import java.time.Duration;
@@ -62,9 +61,8 @@ public static void registerEvents() {
6261
log.debug("Smap entry events registered successfully");
6362
}
6463
} catch (Exception e) {
65-
log.debug(
66-
SEND_TELEMETRY,
67-
"Smap entry events could not be registered due to missing systemMap operation");
64+
ProfilerFlareLogger.getInstance()
65+
.log("Smap entry events could not be registered due to missing systemMap operation", e);
6866
}
6967
}
7068
}
Lines changed: 265 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,265 @@
1+
package com.datadog.profiling.controller;
2+
3+
import datadog.environment.JavaVirtualMachine;
4+
import datadog.environment.OperatingSystem;
5+
import datadog.environment.SystemProperties;
6+
import de.thetaphi.forbiddenapis.SuppressForbidden;
7+
import java.io.File;
8+
import java.io.IOException;
9+
import java.net.URL;
10+
import java.nio.file.FileSystems;
11+
import java.nio.file.FileVisitResult;
12+
import java.nio.file.FileVisitor;
13+
import java.nio.file.Files;
14+
import java.nio.file.Path;
15+
import java.nio.file.Paths;
16+
import java.nio.file.attribute.BasicFileAttributes;
17+
import java.nio.file.attribute.PosixFilePermissions;
18+
import java.util.Set;
19+
import java.util.function.Supplier;
20+
import java.util.jar.JarFile;
21+
22+
public final class EnvironmentChecker {
23+
private static void appendLine(String line, StringBuilder sb) {
24+
sb.append(line).append(System.lineSeparator());
25+
}
26+
27+
private static void appendLine(Supplier<StringBuilder> sbSupplier) {
28+
sbSupplier.get().append(System.lineSeparator());
29+
}
30+
31+
@SuppressForbidden
32+
public static boolean checkEnvironment(String temp, StringBuilder sb) {
33+
if (!JavaVirtualMachine.isJavaVersionAtLeast(8)) {
34+
appendLine("Profiler requires Java 8 or newer", sb);
35+
return false;
36+
}
37+
appendLine(
38+
() ->
39+
sb.append("Using Java version: ")
40+
.append(JavaVirtualMachine.getRuntimeVersion())
41+
.append(" (")
42+
.append(SystemProperties.getOrDefault("java.home", "unknown"))
43+
.append(")"));
44+
appendLine(
45+
() ->
46+
sb.append("Running as user: ")
47+
.append(SystemProperties.getOrDefault("user.name", "unknown")));
48+
boolean result = false;
49+
result |= checkJFR(sb);
50+
result |= checkDdprof(sb);
51+
if (!result) {;
52+
appendLine("Profiler is not supported on this JVM.", sb);
53+
return false;
54+
} else {
55+
appendLine("Profiler is supported on this JVM.", sb);
56+
}
57+
sb.append(System.lineSeparator());
58+
if (!checkTempLocation(temp, sb)) {
59+
appendLine("Profiler will not work properly due to issues with temp directory location.", sb);
60+
return false;
61+
} else {
62+
if (!temp.equals(SystemProperties.get("java.io.tmpdir"))) {
63+
appendLine(
64+
() ->
65+
sb.append("! Make sure to add '-Ddd.profiling.tempdir=")
66+
.append(temp)
67+
.append("' to your JVM command line !"));
68+
}
69+
}
70+
appendLine("Profiler is ready to be used.", sb);
71+
return true;
72+
}
73+
74+
@SuppressForbidden
75+
private static boolean checkJFR(StringBuilder sb) {
76+
if (JavaVirtualMachine.isOracleJDK8()) {
77+
appendLine(
78+
"JFR is commercial feature in Oracle JDK 8. Make sure you have the right license.", sb);
79+
return true;
80+
} else if (JavaVirtualMachine.isJ9()) {
81+
appendLine("JFR is not supported on J9 JVM.", sb);
82+
return false;
83+
} else {
84+
appendLine(
85+
() -> sb.append("JFR is supported on ").append(JavaVirtualMachine.getRuntimeVersion()));
86+
return true;
87+
}
88+
}
89+
90+
@SuppressForbidden
91+
private static boolean checkDdprof(StringBuilder sb) {
92+
if (!OperatingSystem.isLinux()) {
93+
appendLine("Datadog profiler is only supported on Linux.", sb);
94+
return false;
95+
} else {
96+
appendLine(
97+
() ->
98+
sb.append("Datadog profiler is supported on ")
99+
.append(JavaVirtualMachine.getRuntimeVersion()));
100+
return true;
101+
}
102+
}
103+
104+
@SuppressForbidden
105+
private static boolean checkTempLocation(String temp, StringBuilder sb) {
106+
// Check if the temp directory is writable
107+
if (temp == null || temp.isEmpty()) {
108+
appendLine("Temp directory is not specified.", sb);
109+
return false;
110+
}
111+
112+
appendLine(() -> sb.append("Checking temporary directory: ").append(temp));
113+
114+
Path base = Paths.get(temp);
115+
if (!Files.exists(base)) {
116+
appendLine(() -> sb.append("Temporary directory does not exist: ").append(base));
117+
return false;
118+
}
119+
Path target = base.resolve("dd-profiler").normalize();
120+
boolean rslt = true;
121+
Set<String> supportedViews = FileSystems.getDefault().supportedFileAttributeViews();
122+
boolean isPosix = supportedViews.contains("posix");
123+
try {
124+
if (isPosix) {
125+
Files.createDirectories(
126+
target,
127+
PosixFilePermissions.asFileAttribute(PosixFilePermissions.fromString("rwx------")));
128+
} else {
129+
// non-posix, eg. Windows - let's rely on the created folders being world-writable
130+
Files.createDirectories(target);
131+
}
132+
appendLine(() -> sb.append("Temporary directory is writable: ").append(target));
133+
rslt &= checkCreateTempFile(target, sb);
134+
rslt &= checkLoadLibrary(target, sb);
135+
} catch (Exception e) {
136+
appendLine(() -> sb.append("Unable to create temp directory in location ").append(temp));
137+
if (isPosix) {
138+
appendLine(
139+
() ->
140+
sb.append("Base dir: ")
141+
.append(base)
142+
.append(" [")
143+
.append(getPermissionsStringSafe(base))
144+
.append("]"));
145+
}
146+
appendLine(() -> sb.append("Error: ").append(e));
147+
} finally {
148+
if (Files.exists(target)) {
149+
try {
150+
Files.walkFileTree(
151+
target,
152+
new FileVisitor<Path>() {
153+
@Override
154+
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
155+
throws IOException {
156+
return FileVisitResult.CONTINUE;
157+
}
158+
159+
@Override
160+
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
161+
throws IOException {
162+
Files.delete(file);
163+
return FileVisitResult.CONTINUE;
164+
}
165+
166+
@Override
167+
public FileVisitResult visitFileFailed(Path file, IOException exc)
168+
throws IOException {
169+
return FileVisitResult.CONTINUE;
170+
}
171+
172+
@Override
173+
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
174+
throws IOException {
175+
Files.delete(dir);
176+
return FileVisitResult.CONTINUE;
177+
}
178+
});
179+
} catch (IOException ignored) {
180+
// should never happen
181+
}
182+
}
183+
}
184+
return rslt;
185+
}
186+
187+
private static String getPermissionsStringSafe(Path file) {
188+
try {
189+
return PosixFilePermissions.toString(Files.getPosixFilePermissions(file));
190+
} catch (IOException ignored) {
191+
return "<unavailable>";
192+
}
193+
}
194+
195+
@SuppressForbidden
196+
private static boolean checkCreateTempFile(Path target, StringBuilder sb) {
197+
// create a file to check if the directory is writable
198+
try {
199+
appendLine(() -> sb.append("Attempting to create a test file in: ").append(target));
200+
Path testFile = target.resolve("testfile");
201+
Files.createFile(testFile);
202+
appendLine(() -> sb.append("Test file created: ").append(testFile));
203+
return true;
204+
} catch (Exception e) {
205+
appendLine(() -> sb.append("Unable to create test file in temp directory ").append(target));
206+
appendLine(() -> sb.append("Error: ").append(e));
207+
}
208+
return false;
209+
}
210+
211+
@SuppressForbidden
212+
private static boolean checkLoadLibrary(Path target, StringBuilder sb) {
213+
if (!OperatingSystem.isLinux()) {
214+
// we are loading the native library only on linux
215+
appendLine("Skipping native library check on non-linux platform", sb);
216+
return true;
217+
}
218+
boolean rslt = true;
219+
try {
220+
rslt &= extractSoFromJar(target, sb);
221+
if (rslt) {
222+
Path libFile = target.resolve("libjavaProfiler.so");
223+
appendLine(() -> sb.append("Attempting to load native library from: ").append(libFile));
224+
System.load(libFile.toString());
225+
appendLine("Native library loaded successfully", sb);
226+
}
227+
return true;
228+
} catch (Throwable t) {
229+
appendLine(
230+
() -> sb.append("Unable to load native library in temp directory ").append(target));
231+
appendLine(() -> sb.append("Error: ").append(t));
232+
return false;
233+
}
234+
}
235+
236+
@SuppressForbidden
237+
private static boolean extractSoFromJar(Path target, StringBuilder sb) throws Exception {
238+
URL jarUrl = EnvironmentChecker.class.getProtectionDomain().getCodeSource().getLocation();
239+
try (JarFile jarFile = new JarFile(new File(jarUrl.toURI()))) {
240+
return jarFile.stream()
241+
.filter(e -> e.getName().contains("libjavaProfiler.so"))
242+
.filter(
243+
e ->
244+
e.getName()
245+
.contains(OperatingSystem.isAarch64() ? "/linux-arm64/" : "/linux-x64/")
246+
&& (!OperatingSystem.isMusl() || e.getName().contains("-musl")))
247+
.findFirst()
248+
.map(
249+
e -> {
250+
try {
251+
Path soFile = target.resolve("libjavaProfiler.so");
252+
Files.createDirectories(soFile.getParent());
253+
Files.copy(jarFile.getInputStream(e), soFile);
254+
appendLine(() -> sb.append("Native library extracted to: ").append(soFile));
255+
return true;
256+
} catch (Throwable t) {
257+
appendLine("Failed to extract or load native library", sb);
258+
appendLine(() -> sb.append("Error: ").append(t));
259+
}
260+
return false;
261+
})
262+
.orElse(Boolean.FALSE);
263+
}
264+
}
265+
}

0 commit comments

Comments
 (0)