mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8347287: JFR: Remove use of Security Manager
Reviewed-by: mgronlun
This commit is contained in:
parent
12752b0031
commit
ec7393e919
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -49,7 +49,7 @@ class JfrIntrinsicSupport : AllStatic {
|
||||
#define JFR_TEMPLATES(template) \
|
||||
template(jdk_jfr_internal_management_HiddenWait, "jdk/jfr/internal/management/HiddenWait") \
|
||||
template(jdk_jfr_internal_JVM, "jdk/jfr/internal/JVM") \
|
||||
template(jdk_jfr_internal_event_EventWriterFactory, "jdk/jfr/internal/event/EventWriterFactory") \
|
||||
template(jdk_jfr_internal_event_EventWriter, "jdk/jfr/internal/event/EventWriter") \
|
||||
template(jdk_jfr_internal_event_EventConfiguration_signature, "Ljdk/jfr/internal/event/EventConfiguration;") \
|
||||
template(getEventWriter_signature, "()Ljdk/jfr/internal/event/EventWriter;") \
|
||||
template(eventConfiguration_name, "eventConfiguration") \
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -182,7 +182,7 @@ static inline const Method* ljf_sender_method(JavaThread* jt) {
|
||||
return ljf.method();
|
||||
}
|
||||
|
||||
static const char* const link_error_msg = "illegal access linking method 'jdk.jfr.internal.event.EventWriterFactory.getEventWriter(long)'";
|
||||
static const char* const link_error_msg = "illegal access linking method 'jdk.jfr.internal.event.EventWriter.getEventWriter()'";
|
||||
|
||||
void JfrResolution::on_runtime_resolution(const CallInfo & info, TRAPS) {
|
||||
assert(info.selected_method() != nullptr, "invariant");
|
||||
@ -199,12 +199,12 @@ void JfrResolution::on_runtime_resolution(const CallInfo & info, TRAPS) {
|
||||
if (method->name() != event_writer_method_name) {
|
||||
return;
|
||||
}
|
||||
static const Symbol* const event_writer_factory_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriterFactory();
|
||||
assert(event_writer_factory_klass_name != nullptr, "invariant");
|
||||
if (info.resolved_klass()->name() != event_writer_factory_klass_name) {
|
||||
static const Symbol* const event_writer_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriter();
|
||||
assert(event_writer_klass_name != nullptr, "invariant");
|
||||
if (info.resolved_klass()->name() != event_writer_klass_name) {
|
||||
return;
|
||||
}
|
||||
// Attempting to link against jdk.jfr.internal.event.EventWriterFactory.getEventWriter().
|
||||
// Attempting to link against jdk.jfr.internal.event.EventWriter.getEventWriter().
|
||||
// The sender, i.e. the method attempting to link, is in the ljf (if one exists).
|
||||
const Method* const sender = ljf_sender_method(THREAD);
|
||||
if (sender == nullptr) {
|
||||
@ -228,9 +228,9 @@ void JfrResolution::on_runtime_resolution(const CallInfo & info, TRAPS) {
|
||||
}
|
||||
|
||||
static inline bool is_compiler_linking_event_writer(const Symbol* holder, const Symbol* name) {
|
||||
static const Symbol* const event_writer_factory_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriterFactory();
|
||||
assert(event_writer_factory_klass_name != nullptr, "invariant");
|
||||
if (holder != event_writer_factory_klass_name) {
|
||||
static const Symbol* const event_writer_klass_name = vmSymbols::jdk_jfr_internal_event_EventWriter();
|
||||
assert(event_writer_klass_name != nullptr, "invariant");
|
||||
if (holder != event_writer_klass_name) {
|
||||
return false;
|
||||
}
|
||||
static const Symbol* const event_writer_method_name = vmSymbols::getEventWriter_name();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -37,7 +37,6 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.TypeLibrary;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
@ -115,7 +114,6 @@ public final class AnnotationElement {
|
||||
public AnnotationElement(Class<? extends Annotation> annotationType, Map<String, Object> values) {
|
||||
Objects.requireNonNull(annotationType, "annotationType");
|
||||
Objects.requireNonNull(values, "values");
|
||||
SecuritySupport.checkRegisterPermission();
|
||||
// copy values to avoid modification after validation
|
||||
HashMap<String, Object> map = new HashMap<>(values);
|
||||
for (Map.Entry<String, Object> entry : map.entrySet()) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -37,7 +37,6 @@ import jdk.internal.module.Checks;
|
||||
import jdk.jfr.internal.EventClassBuilder;
|
||||
import jdk.jfr.internal.JVMSupport;
|
||||
import jdk.jfr.internal.MetadataRepository;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
|
||||
@ -99,8 +98,6 @@ public final class EventFactory {
|
||||
Objects.requireNonNull(fields, "fields");
|
||||
JVMSupport.ensureWithInternalError();
|
||||
|
||||
SecuritySupport.checkRegisterPermission();
|
||||
|
||||
List<AnnotationElement> sanitizedAnnotation = Utils.sanitizeNullFreeList(annotationElements, AnnotationElement.class);
|
||||
List<ValueDescriptor> sanitizedFields = Utils.sanitizeNullFreeList(fields, ValueDescriptor.class);
|
||||
Set<String> nameSet = HashSet.newHashSet(sanitizedFields.size());
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,8 +29,6 @@ import static jdk.jfr.internal.LogLevel.DEBUG;
|
||||
import static jdk.jfr.internal.LogLevel.INFO;
|
||||
import static jdk.jfr.internal.LogTag.JFR;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
@ -44,7 +42,6 @@ import jdk.jfr.internal.Options;
|
||||
import jdk.jfr.internal.PlatformRecorder;
|
||||
import jdk.jfr.internal.PlatformRecording;
|
||||
import jdk.jfr.internal.Repository;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
import jdk.jfr.internal.periodic.PeriodicEvents;
|
||||
|
||||
@ -157,7 +154,6 @@ public final class FlightRecorder {
|
||||
*/
|
||||
public static FlightRecorder getFlightRecorder() throws IllegalStateException {
|
||||
synchronized (PlatformRecorder.class) {
|
||||
SecuritySupport.checkAccessFlightRecorder();
|
||||
JVMSupport.ensureWithIllegalStateException();
|
||||
if (platformRecorder == null) {
|
||||
try {
|
||||
@ -213,10 +209,7 @@ public final class FlightRecorder {
|
||||
}
|
||||
|
||||
Utils.ensureValidEventSubclass(eventClass);
|
||||
SecuritySupport.checkRegisterPermission();
|
||||
@SuppressWarnings("removal")
|
||||
AccessControlContext acc = AccessController.getContext();
|
||||
PeriodicEvents.addUserEvent(acc, eventClass, hook);
|
||||
PeriodicEvents.addJavaEvent(eventClass, hook);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -227,7 +220,6 @@ public final class FlightRecorder {
|
||||
*/
|
||||
public static boolean removePeriodicEvent(Runnable hook) {
|
||||
Objects.requireNonNull(hook, "hook");
|
||||
SecuritySupport.checkRegisterPermission();
|
||||
if (JVMSupport.isNotAvailable()) {
|
||||
return false;
|
||||
}
|
||||
@ -260,7 +252,6 @@ public final class FlightRecorder {
|
||||
*/
|
||||
public static void addListener(FlightRecorderListener changeListener) {
|
||||
Objects.requireNonNull(changeListener, "changeListener");
|
||||
SecuritySupport.checkAccessFlightRecorder();
|
||||
if (JVMSupport.isNotAvailable()) {
|
||||
return;
|
||||
}
|
||||
@ -280,7 +271,6 @@ public final class FlightRecorder {
|
||||
*/
|
||||
public static boolean removeListener(FlightRecorderListener changeListener) {
|
||||
Objects.requireNonNull(changeListener, "changeListener");
|
||||
SecuritySupport.checkAccessFlightRecorder();
|
||||
if (JVMSupport.isNotAvailable()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
package jdk.jfr;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@ -34,7 +33,6 @@ import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.PlatformRecorder;
|
||||
import jdk.jfr.internal.PlatformRecording;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.management.EventSettingsModifier;
|
||||
|
||||
@ -156,12 +154,6 @@ public final class FlightRecorderPermission extends java.security.BasicPermissio
|
||||
return FlightRecorder.getFlightRecorder().getInternal();
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
@Override
|
||||
public AccessControlContext getContext(SettingControl settingControl) {
|
||||
return settingControl.getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventSettings newEventSettings(EventSettingsModifier esm) {
|
||||
return new EventSettings.DelegatedEventSettings(esm);
|
||||
@ -184,7 +176,7 @@ public final class FlightRecorderPermission extends java.security.BasicPermissio
|
||||
*/
|
||||
public FlightRecorderPermission(String name) {
|
||||
super(Objects.requireNonNull(name, "name"));
|
||||
if (!name.equals(SecuritySupport.ACCESS_FLIGHT_RECORDER) && !name.equals(SecuritySupport.REGISTER_EVENT)) {
|
||||
if (!name.equals("accessFlightRecorder") && !name.equals("registerEvent")) {
|
||||
throw new IllegalArgumentException("name: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -39,7 +39,7 @@ import jdk.jfr.internal.PlatformRecorder;
|
||||
import jdk.jfr.internal.PlatformRecording;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
import jdk.jfr.internal.WriteableUserPath;
|
||||
import jdk.jfr.internal.WriteablePath;
|
||||
|
||||
/**
|
||||
* Provides means to configure, start, stop and dump recording data to disk.
|
||||
@ -368,7 +368,7 @@ public final class Recording implements Closeable {
|
||||
*/
|
||||
public void dump(Path destination) throws IOException {
|
||||
Objects.requireNonNull(destination, "destination");
|
||||
internal.dump(new WriteableUserPath(destination));
|
||||
internal.dump(new WriteablePath(destination));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -461,7 +461,7 @@ public final class Recording implements Closeable {
|
||||
* @throws IOException if the path is not writable
|
||||
*/
|
||||
public void setDestination(Path destination) throws IOException {
|
||||
internal.setDestination(destination != null ? new WriteableUserPath(destination) : null);
|
||||
internal.setDestination(destination != null ? new WriteablePath(destination) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -471,11 +471,11 @@ public final class Recording implements Closeable {
|
||||
* @return the destination file, or {@code null} if not set.
|
||||
*/
|
||||
public Path getDestination() {
|
||||
WriteableUserPath usp = internal.getDestination();
|
||||
if (usp == null) {
|
||||
WriteablePath wp = internal.getDestination();
|
||||
if (wp == null) {
|
||||
return null;
|
||||
} else {
|
||||
return usp.getPotentiallyMaliciousOriginal();
|
||||
return wp.getPath();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,12 +25,8 @@
|
||||
|
||||
package jdk.jfr;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.internal.settings.JDKSettingControl;
|
||||
|
||||
/**
|
||||
* Base class to extend to create setting controls.
|
||||
* <p>
|
||||
@ -77,30 +73,10 @@ import jdk.jfr.internal.settings.JDKSettingControl;
|
||||
@MetadataDefinition
|
||||
public abstract class SettingControl {
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private final AccessControlContext context;
|
||||
private final boolean initialized;
|
||||
|
||||
/**
|
||||
* Constructor for invocation by subclass constructors.
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
protected SettingControl() {
|
||||
context = this instanceof JDKSettingControl ? null : AccessController.getContext();
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
final AccessControlContext getContext() {
|
||||
// Ensure object state is safe
|
||||
if (!initialized) {
|
||||
throw new InternalError("Object must be initialized before security context can be retrieved");
|
||||
}
|
||||
AccessControlContext c = this.context;
|
||||
if (c == null && !(this instanceof JDKSettingControl)) {
|
||||
throw new InternalError("Security context can only be null for trusted setting controls");
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,7 +31,6 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.jfr.internal.AnnotationConstruct;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
|
||||
@ -143,7 +142,6 @@ public final class ValueDescriptor {
|
||||
Objects.requireNonNull(type, "type");
|
||||
Objects.requireNonNull(name, "name");
|
||||
Objects.requireNonNull(annotations, "annotations");
|
||||
SecuritySupport.checkRegisterPermission();
|
||||
if (!allowArray) {
|
||||
if (type.isArray()) {
|
||||
throw new IllegalArgumentException("Array types are not allowed");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,18 +27,14 @@ package jdk.jfr.consumer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.consumer.EventDirectoryStream;
|
||||
import jdk.jfr.internal.consumer.EventFileStream;
|
||||
import jdk.jfr.internal.consumer.FileAccess;
|
||||
|
||||
/**
|
||||
* Represents a stream of events.
|
||||
@ -113,13 +109,9 @@ public interface EventStream extends AutoCloseable {
|
||||
* @throws IOException if a stream can't be opened, or an I/O error occurs
|
||||
* when trying to access the repository
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
public static EventStream openRepository() throws IOException {
|
||||
SecuritySupport.checkAccessFlightRecorder();
|
||||
return new EventDirectoryStream(
|
||||
AccessController.getContext(),
|
||||
null,
|
||||
SecuritySupport.PRIVILEGED,
|
||||
null,
|
||||
Collections.emptyList(),
|
||||
false
|
||||
@ -143,12 +135,8 @@ public interface EventStream extends AutoCloseable {
|
||||
*/
|
||||
public static EventStream openRepository(Path directory) throws IOException {
|
||||
Objects.requireNonNull(directory, "directory");
|
||||
@SuppressWarnings("removal")
|
||||
AccessControlContext acc = AccessController.getContext();
|
||||
return new EventDirectoryStream(
|
||||
acc,
|
||||
directory,
|
||||
FileAccess.UNPRIVILEGED,
|
||||
null,
|
||||
Collections.emptyList(),
|
||||
true
|
||||
@ -169,10 +157,9 @@ public interface EventStream extends AutoCloseable {
|
||||
* @throws IOException if the file can't be opened, or an I/O error occurs
|
||||
* during reading
|
||||
*/
|
||||
@SuppressWarnings("removal")
|
||||
static EventStream openFile(Path file) throws IOException {
|
||||
Objects.requireNonNull(file, "file");
|
||||
return new EventFileStream(AccessController.getContext(), file);
|
||||
return new EventFileStream(file);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -43,7 +43,6 @@ import jdk.jfr.internal.consumer.ChunkParser.ParserConfiguration;
|
||||
import jdk.jfr.internal.consumer.ParserFilter;
|
||||
import jdk.jfr.internal.consumer.ChunkHeader;
|
||||
import jdk.jfr.internal.consumer.ChunkParser;
|
||||
import jdk.jfr.internal.consumer.FileAccess;
|
||||
import jdk.jfr.internal.consumer.ParserState;
|
||||
import jdk.jfr.internal.consumer.RecordingInput;
|
||||
import jdk.jfr.internal.consumer.filter.ChunkWriter;
|
||||
@ -81,7 +80,7 @@ public final class RecordingFile implements Closeable {
|
||||
public RecordingFile(Path file) throws IOException {
|
||||
Objects.requireNonNull(file, "file");
|
||||
this.file = file.toFile();
|
||||
this.input = new RecordingInput(this.file, FileAccess.UNPRIVILEGED);
|
||||
this.input = new RecordingInput(this.file);
|
||||
this.chunkWriter = null;
|
||||
findNext();
|
||||
}
|
||||
@ -146,7 +145,7 @@ public final class RecordingFile implements Closeable {
|
||||
MetadataDescriptor previous = null;
|
||||
List<EventType> types = new ArrayList<>();
|
||||
HashSet<Long> foundIds = new HashSet<>();
|
||||
try (RecordingInput ri = new RecordingInput(file, FileAccess.UNPRIVILEGED)) {
|
||||
try (RecordingInput ri = new RecordingInput(file)) {
|
||||
ChunkHeader ch = new ChunkHeader(ri);
|
||||
aggregateEventTypeForChunk(ch, null, types, foundIds);
|
||||
while (!ch.isLastChunk()) {
|
||||
@ -162,7 +161,7 @@ public final class RecordingFile implements Closeable {
|
||||
MetadataDescriptor previous = null;
|
||||
List<Type> types = new ArrayList<>(200);
|
||||
HashSet<Long> foundIds = HashSet.newHashSet(types.size());
|
||||
try (RecordingInput ri = new RecordingInput(file, FileAccess.UNPRIVILEGED)) {
|
||||
try (RecordingInput ri = new RecordingInput(file)) {
|
||||
ChunkHeader ch = new ChunkHeader(ri);
|
||||
ch.awaitFinished();
|
||||
aggregateTypeForChunk(ch, null, types, foundIds);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,8 +27,6 @@ package jdk.jfr.consumer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.Collections;
|
||||
@ -45,7 +43,6 @@ import jdk.jfr.Recording;
|
||||
import jdk.jfr.RecordingState;
|
||||
import jdk.jfr.internal.PlatformRecording;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
import jdk.jfr.internal.consumer.EventDirectoryStream;
|
||||
import jdk.jfr.internal.management.StreamBarrier;
|
||||
@ -98,18 +95,13 @@ public final class RecordingStream implements AutoCloseable, EventStream {
|
||||
}
|
||||
|
||||
private RecordingStream(Map<String, String> settings) {
|
||||
SecuritySupport.checkAccessFlightRecorder();
|
||||
@SuppressWarnings("removal")
|
||||
AccessControlContext acc = AccessController.getContext();
|
||||
this.recording = new Recording();
|
||||
this.creationTime = Instant.now();
|
||||
this.recording.setName("Recording Stream: " + creationTime);
|
||||
try {
|
||||
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
|
||||
this.directoryStream = new EventDirectoryStream(
|
||||
acc,
|
||||
null,
|
||||
SecuritySupport.PRIVILEGED,
|
||||
pr,
|
||||
configurations(),
|
||||
false
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,6 +28,7 @@ package jdk.jfr.internal;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -65,7 +66,7 @@ final class ChunkInputStream extends InputStream {
|
||||
return false;
|
||||
}
|
||||
|
||||
stream = new BufferedInputStream(SecuritySupport.newFileInputStream(currentChunk.getFile()));
|
||||
stream = new BufferedInputStream(Files.newInputStream(currentChunk.getFile()));
|
||||
unstreamedSize -= currentChunk.getSize();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,23 +25,16 @@
|
||||
|
||||
package jdk.jfr.internal;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.internal.settings.JDKSettingControl;
|
||||
import jdk.jfr.internal.settings.PeriodSetting;
|
||||
import jdk.jfr.internal.settings.StackTraceSetting;
|
||||
import jdk.jfr.internal.settings.ThresholdSetting;
|
||||
|
||||
final class Control {
|
||||
@SuppressWarnings("removal")
|
||||
private final AccessControlContext context;
|
||||
private static final int CACHE_SIZE = 5;
|
||||
private final Set<?>[] cachedUnions = new HashSet<?>[CACHE_SIZE];
|
||||
private final String[] cachedValues = new String[CACHE_SIZE];
|
||||
@ -51,12 +44,8 @@ final class Control {
|
||||
|
||||
// called by exposed subclass in external API
|
||||
public Control(SettingControl delegate, String defaultValue) {
|
||||
this.context = PrivateAccess.getInstance().getContext(delegate);
|
||||
this.delegate = delegate;
|
||||
this.defaultValue = defaultValue;
|
||||
if (this.context == null && !(delegate instanceof JDKSettingControl)) {
|
||||
throw new InternalError("Security context can only be null for trusted setting controls");
|
||||
}
|
||||
}
|
||||
|
||||
boolean isType(Class<? extends SettingControl> clazz) {
|
||||
@ -74,25 +63,8 @@ final class Control {
|
||||
apply(defaultValue);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public String getValue() {
|
||||
if (context == null) {
|
||||
// VM events requires no access control context
|
||||
return delegate.getValue();
|
||||
} else {
|
||||
return AccessController.doPrivileged(new PrivilegedAction<String>() {
|
||||
@Override
|
||||
public String run() {
|
||||
try {
|
||||
return delegate.getValue();
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when trying to get value for " + getClass());
|
||||
}
|
||||
return defaultValue != null ? defaultValue : ""; // Need to return something
|
||||
}
|
||||
}, context);
|
||||
}
|
||||
return delegate.getValue();
|
||||
}
|
||||
|
||||
private void apply(String value) {
|
||||
@ -102,53 +74,18 @@ final class Control {
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public void setValue(String value) {
|
||||
if (context == null) {
|
||||
// VM events requires no access control context
|
||||
try {
|
||||
delegate.setValue(value);
|
||||
lastValue = delegate.getValue();
|
||||
} catch (Throwable t) {
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when setting value \"" + value + "\" for " + getClass());
|
||||
lastValue = null;
|
||||
}
|
||||
} else {
|
||||
lastValue = AccessController.doPrivileged(new PrivilegedAction<String>() {
|
||||
@Override
|
||||
public String run() {
|
||||
try {
|
||||
delegate.setValue(value);
|
||||
return delegate.getValue();
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when setting value \"" + value + "\" for " + getClass());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, context);
|
||||
try {
|
||||
delegate.setValue(value);
|
||||
lastValue = delegate.getValue();
|
||||
} catch (Throwable t) {
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when setting value \"" + value + "\". " + t.getMessage());
|
||||
lastValue = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public String combine(Set<String> values) {
|
||||
if (context == null) {
|
||||
// VM events requires no access control context
|
||||
return delegate.combine(values);
|
||||
}
|
||||
return AccessController.doPrivileged(new PrivilegedAction<String>() {
|
||||
@Override
|
||||
public String run() {
|
||||
try {
|
||||
return delegate.combine(Collections.unmodifiableSet(values));
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occurred when combining " + values + " for " + getClass());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}, context);
|
||||
return delegate.combine(values);
|
||||
}
|
||||
|
||||
private final String findCombine(Set<String> values) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -280,7 +280,7 @@ public final class EventControl {
|
||||
} catch (Exception e) {
|
||||
throw (Error) new InternalError("Could not get constructor for " + settingControlClass.getName()).initCause(e);
|
||||
}
|
||||
SecuritySupport.setAccessible(cc);
|
||||
cc.setAccessible(true);
|
||||
try {
|
||||
return (SettingControl) cc.newInstance();
|
||||
} catch (IllegalArgumentException | InvocationTargetException e) {
|
||||
@ -373,13 +373,6 @@ public final class EventControl {
|
||||
return idName;
|
||||
}
|
||||
|
||||
/**
|
||||
* A malicious user must never be able to run a callback in the wrong
|
||||
* context. Methods on SettingControl must therefore never be invoked directly
|
||||
* by JFR, instead use jdk.jfr.internal.Control.
|
||||
*
|
||||
* The returned list is only to be used inside EventConfiguration
|
||||
*/
|
||||
public List<SettingControl> getSettingControls() {
|
||||
return settingControls;
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -89,7 +89,6 @@ final class EventInstrumentation {
|
||||
private static final ClassDesc TYPE_EVENT_CONFIGURATION = classDesc(EventConfiguration.class);
|
||||
private static final ClassDesc TYPE_ISE = Bytecode.classDesc(IllegalStateException.class);
|
||||
private static final ClassDesc TYPE_EVENT_WRITER = classDesc(EventWriter.class);
|
||||
private static final ClassDesc TYPE_EVENT_WRITER_FACTORY = ClassDesc.of("jdk.jfr.internal.event.EventWriterFactory");
|
||||
private static final ClassDesc TYPE_OBJECT = Bytecode.classDesc(Object.class);
|
||||
private static final ClassDesc TYPE_SETTING_DEFINITION = Bytecode.classDesc(SettingDefinition.class);
|
||||
private static final MethodDesc METHOD_BEGIN = MethodDesc.of("begin", "()V");
|
||||
@ -100,7 +99,7 @@ final class EventInstrumentation {
|
||||
private static final MethodDesc METHOD_EVENT_CONFIGURATION_SHOULD_COMMIT = MethodDesc.of("shouldCommit", "(J)Z");
|
||||
private static final MethodDesc METHOD_EVENT_CONFIGURATION_GET_SETTING = MethodDesc.of("getSetting", SettingControl.class, int.class);
|
||||
private static final MethodDesc METHOD_EVENT_SHOULD_COMMIT = MethodDesc.of("shouldCommit", "()Z");
|
||||
private static final MethodDesc METHOD_GET_EVENT_WRITER_KEY = MethodDesc.of("getEventWriter", "(J)" + TYPE_EVENT_WRITER.descriptorString());
|
||||
private static final MethodDesc METHOD_GET_EVENT_WRITER = MethodDesc.of("getEventWriter", "()" + TYPE_EVENT_WRITER.descriptorString());
|
||||
private static final MethodDesc METHOD_IS_ENABLED = MethodDesc.of("isEnabled", "()Z");
|
||||
private static final MethodDesc METHOD_RESET = MethodDesc.of("reset", "()V");
|
||||
private static final MethodDesc METHOD_SHOULD_COMMIT_LONG = MethodDesc.of("shouldCommit", "(J)Z");
|
||||
@ -767,8 +766,7 @@ final class EventInstrumentation {
|
||||
}
|
||||
|
||||
private void getEventWriter(CodeBuilder codeBuilder) {
|
||||
codeBuilder.ldc(EventWriterKey.getKey());
|
||||
invokestatic(codeBuilder, TYPE_EVENT_WRITER_FACTORY, METHOD_GET_EVENT_WRITER_KEY);
|
||||
invokestatic(codeBuilder, TYPE_EVENT_WRITER, METHOD_GET_EVENT_WRITER);
|
||||
}
|
||||
|
||||
private void getEventConfiguration(CodeBuilder codeBuilder) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,19 +26,19 @@
|
||||
package jdk.jfr.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.SequencedSet;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
|
||||
// This class keeps track of files that can't be deleted
|
||||
// so they can at a later staged be removed.
|
||||
final class FilePurger {
|
||||
|
||||
private static final SequencedSet<SafePath> paths = new LinkedHashSet<>();
|
||||
private static final SequencedSet<Path> paths = new LinkedHashSet<>();
|
||||
|
||||
public static synchronized void add(SafePath p) {
|
||||
public static synchronized void add(Path p) {
|
||||
paths.add(p);
|
||||
if (paths.size() > 1000) {
|
||||
removeOldest();
|
||||
@ -50,7 +50,7 @@ final class FilePurger {
|
||||
return;
|
||||
}
|
||||
|
||||
for (SafePath p : new ArrayList<>(paths)) {
|
||||
for (Path p : new ArrayList<>(paths)) {
|
||||
if (delete(p)) {
|
||||
paths.remove(p);
|
||||
}
|
||||
@ -61,16 +61,12 @@ final class FilePurger {
|
||||
paths.removeFirst();
|
||||
}
|
||||
|
||||
private static boolean delete(SafePath p) {
|
||||
try {
|
||||
if (!SecuritySupport.exists(p)) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// ignore
|
||||
private static boolean delete(Path p) {
|
||||
if (!Files.exists(p)) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
SecuritySupport.delete(p);
|
||||
Files.delete(p);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -91,11 +91,11 @@ public final class JDKEvents {
|
||||
try {
|
||||
if (initializationTriggered == false) {
|
||||
for (Class<?> eventClass : eventClasses) {
|
||||
SecuritySupport.registerEvent((Class<? extends Event>) eventClass);
|
||||
MetadataRepository.getInstance().register((Class<? extends Event>) eventClass);
|
||||
}
|
||||
PeriodicEvents.addJDKEvent(jdk.internal.event.ExceptionStatisticsEvent.class, emitExceptionStatistics);
|
||||
PeriodicEvents.addJDKEvent(DirectBufferStatisticsEvent.class, emitDirectBufferStatistics);
|
||||
PeriodicEvents.addJDKEvent(InitialSecurityPropertyEvent.class, emitInitialSecurityProperties);
|
||||
PeriodicEvents.addJavaEvent(jdk.internal.event.ExceptionStatisticsEvent.class, emitExceptionStatistics);
|
||||
PeriodicEvents.addJavaEvent(DirectBufferStatisticsEvent.class, emitDirectBufferStatistics);
|
||||
PeriodicEvents.addJavaEvent(InitialSecurityPropertyEvent.class, emitInitialSecurityProperties);
|
||||
|
||||
initializeContainerEvents();
|
||||
JFRTracing.enable();
|
||||
@ -116,17 +116,21 @@ public final class JDKEvents {
|
||||
}
|
||||
// The registration of events and hooks are needed to provide metadata,
|
||||
// even when not running in a container
|
||||
SecuritySupport.registerEvent(ContainerConfigurationEvent.class);
|
||||
SecuritySupport.registerEvent(ContainerCPUUsageEvent.class);
|
||||
SecuritySupport.registerEvent(ContainerCPUThrottlingEvent.class);
|
||||
SecuritySupport.registerEvent(ContainerMemoryUsageEvent.class);
|
||||
SecuritySupport.registerEvent(ContainerIOUsageEvent.class);
|
||||
registerEvent(ContainerConfigurationEvent.class);
|
||||
registerEvent(ContainerCPUUsageEvent.class);
|
||||
registerEvent(ContainerCPUThrottlingEvent.class);
|
||||
registerEvent(ContainerMemoryUsageEvent.class);
|
||||
registerEvent(ContainerIOUsageEvent.class);
|
||||
|
||||
PeriodicEvents.addJDKEvent(ContainerConfigurationEvent.class, emitContainerConfiguration);
|
||||
PeriodicEvents.addJDKEvent(ContainerCPUUsageEvent.class, emitContainerCPUUsage);
|
||||
PeriodicEvents.addJDKEvent(ContainerCPUThrottlingEvent.class, emitContainerCPUThrottling);
|
||||
PeriodicEvents.addJDKEvent(ContainerMemoryUsageEvent.class, emitContainerMemoryUsage);
|
||||
PeriodicEvents.addJDKEvent(ContainerIOUsageEvent.class, emitContainerIOUsage);
|
||||
PeriodicEvents.addJavaEvent(ContainerConfigurationEvent.class, emitContainerConfiguration);
|
||||
PeriodicEvents.addJavaEvent(ContainerCPUUsageEvent.class, emitContainerCPUUsage);
|
||||
PeriodicEvents.addJavaEvent(ContainerCPUThrottlingEvent.class, emitContainerCPUThrottling);
|
||||
PeriodicEvents.addJavaEvent(ContainerMemoryUsageEvent.class, emitContainerMemoryUsage);
|
||||
PeriodicEvents.addJavaEvent(ContainerIOUsageEvent.class, emitContainerIOUsage);
|
||||
}
|
||||
|
||||
private static void registerEvent(Class<? extends jdk.internal.event.Event> eventClass) {
|
||||
MetadataRepository.getInstance().register(eventClass);
|
||||
}
|
||||
|
||||
private static void emitExceptionStatistics() {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -56,7 +56,7 @@ public final class JVMSupport {
|
||||
private static boolean checkAvailability() {
|
||||
// set jfr.unsupported.vm to true to test API on an unsupported VM
|
||||
try {
|
||||
if (SecuritySupport.getBooleanProperty("jfr.unsupported.vm")) {
|
||||
if (Boolean.getBoolean("jfr.unsupported.vm")) {
|
||||
return false;
|
||||
}
|
||||
} catch (NoClassDefFoundError cnfe) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -64,7 +64,6 @@ final class JVMUpcalls {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Skipping instrumentation for " + clazz.getName() + " since container support is missing");
|
||||
return oldBytes;
|
||||
}
|
||||
EventWriterKey.ensureEventWriterFactory();
|
||||
EventConfiguration configuration = JVMSupport.getConfiguration(clazz.asSubclass(jdk.internal.event.Event.class));
|
||||
if (configuration == null) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "No event configuration found for " + clazz.getName() + ". Ignoring instrumentation request.");
|
||||
@ -124,7 +123,6 @@ final class JVMUpcalls {
|
||||
return oldBytes;
|
||||
}
|
||||
}
|
||||
EventWriterKey.ensureEventWriterFactory();
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Adding " + (forceInstrumentation ? "forced " : "") + "instrumentation for event type " + eventName + " during initial class load");
|
||||
byte[] bytes = ei.buildInstrumented();
|
||||
Bytecode.log(ei.getClassName() + "(" + traceId + ")", bytes);
|
||||
@ -155,6 +153,8 @@ final class JVMUpcalls {
|
||||
* @return a new thread
|
||||
*/
|
||||
static Thread createRecorderThread(ThreadGroup systemThreadGroup, ClassLoader contextClassLoader) {
|
||||
return SecuritySupport.createRecorderThread(systemThreadGroup, contextClassLoader);
|
||||
Thread thread = new Thread(systemThreadGroup, "JFR Recorder Thread");
|
||||
thread.setContextClassLoader(contextClassLoader);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -191,7 +191,7 @@ public final class MetadataLoader {
|
||||
|
||||
public static List<Type> createTypes() throws IOException {
|
||||
try (DataInputStream dis = new DataInputStream(
|
||||
SecuritySupport.getResourceAsStream("/jdk/jfr/internal/types/metadata.bin"))) {
|
||||
MetadataLoader.class.getResourceAsStream("/jdk/jfr/internal/types/metadata.bin"))) {
|
||||
MetadataLoader ml = new MetadataLoader(dis);
|
||||
return ml.buildTypes();
|
||||
} catch (Exception e) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -123,7 +123,6 @@ public final class MetadataRepository {
|
||||
}
|
||||
|
||||
public synchronized void unregister(Class<? extends Event> eventClass) {
|
||||
SecuritySupport.checkRegisterPermission();
|
||||
EventConfiguration configuration = getConfiguration(eventClass, false);
|
||||
if (configuration != null) {
|
||||
configuration.getPlatformEventType().setRegistered(false);
|
||||
@ -135,7 +134,6 @@ public final class MetadataRepository {
|
||||
}
|
||||
|
||||
public synchronized EventType register(Class<? extends jdk.internal.event.Event> eventClass, List<AnnotationElement> dynamicAnnotations, List<ValueDescriptor> dynamicFields) {
|
||||
SecuritySupport.checkRegisterPermission();
|
||||
if (JVM.isExcluded(eventClass)) {
|
||||
// Event classes are marked as excluded during class load
|
||||
// if they override methods in the jdk.jfr.Event class, i.e. commit().
|
||||
@ -186,7 +184,7 @@ public final class MetadataRepository {
|
||||
if (cachedEventConfigurationConstructor == null) {
|
||||
var argClasses = new Class<?>[] { EventType.class, EventControl.class};
|
||||
Constructor<EventConfiguration> c = EventConfiguration.class.getDeclaredConstructor(argClasses);
|
||||
SecuritySupport.setAccessible(c);
|
||||
c.setAccessible(true);
|
||||
cachedEventConfigurationConstructor = c;
|
||||
}
|
||||
return cachedEventConfigurationConstructor.newInstance(eventType, ec);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,8 +26,9 @@
|
||||
package jdk.jfr.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
import static java.nio.file.LinkOption.*;
|
||||
@ -50,7 +51,7 @@ public final class Options {
|
||||
private static long DEFAULT_THREAD_BUFFER_SIZE;
|
||||
private static final int DEFAULT_STACK_DEPTH = 64;
|
||||
private static final long DEFAULT_MAX_CHUNK_SIZE = 12 * 1024 * 1024;
|
||||
private static final SafePath DEFAULT_DUMP_PATH = null;
|
||||
private static final Path DEFAULT_DUMP_PATH = null;
|
||||
private static final boolean DEFAULT_PRESERVE_REPOSITORY = false;
|
||||
|
||||
private static long memorySize;
|
||||
@ -115,10 +116,10 @@ public final class Options {
|
||||
globalBufferSize = globalBufsize;
|
||||
}
|
||||
|
||||
public static synchronized void setDumpPath(SafePath path) throws IOException {
|
||||
public static synchronized void setDumpPath(Path path) throws IOException {
|
||||
if (path != null) {
|
||||
if (SecuritySupport.isWritable(path)) {
|
||||
path = SecuritySupport.toRealPath(path, NOFOLLOW_LINKS);
|
||||
if (Files.isWritable(path)) {
|
||||
path = path.toRealPath(NOFOLLOW_LINKS);
|
||||
} else {
|
||||
throw new IOException("Cannot write JFR emergency dump to " + path.toString());
|
||||
}
|
||||
@ -126,8 +127,8 @@ public final class Options {
|
||||
JVM.setDumpPath(path == null ? null : path.toString());
|
||||
}
|
||||
|
||||
public static synchronized SafePath getDumpPath() {
|
||||
return new SafePath(JVM.getDumpPath());
|
||||
public static synchronized Path getDumpPath() {
|
||||
return Path.of(JVM.getDumpPath());
|
||||
}
|
||||
|
||||
public static synchronized void setStackDepth(Integer stackTraceDepth) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -33,8 +33,7 @@ import static jdk.jfr.internal.LogTag.JFR;
|
||||
import static jdk.jfr.internal.LogTag.JFR_SYSTEM;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.nio.file.Path;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
@ -46,7 +45,6 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import jdk.jfr.FlightRecorder;
|
||||
import jdk.jfr.FlightRecorderListener;
|
||||
@ -54,8 +52,6 @@ import jdk.jfr.Recording;
|
||||
import jdk.jfr.RecordingState;
|
||||
import jdk.jfr.events.ActiveRecordingEvent;
|
||||
import jdk.jfr.events.ActiveSettingEvent;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.SecuritySupport.SecureRecorderListener;
|
||||
import jdk.jfr.internal.consumer.EventLog;
|
||||
import jdk.jfr.internal.periodic.PeriodicEvents;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
@ -64,7 +60,7 @@ public final class PlatformRecorder {
|
||||
|
||||
|
||||
private final ArrayList<PlatformRecording> recordings = new ArrayList<>();
|
||||
private static final List<SecureRecorderListener> changeListeners = new ArrayList<>();
|
||||
private static final List<FlightRecorderListener> changeListeners = new ArrayList<>();
|
||||
private final Repository repository;
|
||||
private final Thread shutdownHook;
|
||||
|
||||
@ -83,25 +79,9 @@ public final class PlatformRecorder {
|
||||
JDKEvents.initialize();
|
||||
Logger.log(JFR_SYSTEM, INFO, "Registered JDK events");
|
||||
startDiskMonitor();
|
||||
shutdownHook = SecuritySupport.createThreadWitNoPermissions("JFR Shutdown Hook", new ShutdownHook(this));
|
||||
SecuritySupport.setUncaughtExceptionHandler(shutdownHook, new ShutdownHook.ExceptionHandler());
|
||||
SecuritySupport.registerShutdownHook(shutdownHook);
|
||||
}
|
||||
|
||||
|
||||
private static Timer createTimer() {
|
||||
try {
|
||||
List<Timer> result = new CopyOnWriteArrayList<>();
|
||||
Thread t = SecuritySupport.createThreadWitNoPermissions("Permissionless thread", ()-> {
|
||||
result.add(new Timer("JFR Recording Scheduler", true));
|
||||
});
|
||||
JVM.exclude(t);
|
||||
t.start();
|
||||
t.join();
|
||||
return result.getFirst();
|
||||
} catch (InterruptedException e) {
|
||||
throw new IllegalStateException("Not able to create timer task. " + e.getMessage(), e);
|
||||
}
|
||||
shutdownHook = new ShutdownHook(this);
|
||||
shutdownHook.setUncaughtExceptionHandler(new ShutdownHook.ExceptionHandler());
|
||||
Runtime.getRuntime().addShutdownHook(shutdownHook);
|
||||
}
|
||||
|
||||
public synchronized PlatformRecording newRecording(Map<String, String> settings) {
|
||||
@ -138,27 +118,18 @@ public final class PlatformRecorder {
|
||||
}
|
||||
|
||||
public static synchronized void addListener(FlightRecorderListener changeListener) {
|
||||
@SuppressWarnings("removal")
|
||||
AccessControlContext context = AccessController.getContext();
|
||||
SecureRecorderListener sl = new SecureRecorderListener(context, changeListener);
|
||||
boolean runInitialized;
|
||||
synchronized (PlatformRecorder.class) {
|
||||
runInitialized = FlightRecorder.isInitialized();
|
||||
changeListeners.add(sl);
|
||||
changeListeners.add(changeListener);
|
||||
}
|
||||
if (runInitialized) {
|
||||
sl.recorderInitialized(FlightRecorder.getFlightRecorder());
|
||||
changeListener.recorderInitialized(FlightRecorder.getFlightRecorder());
|
||||
}
|
||||
}
|
||||
|
||||
public static synchronized boolean removeListener(FlightRecorderListener changeListener) {
|
||||
for (SecureRecorderListener s : new ArrayList<>(changeListeners)) {
|
||||
if (s.getChangeListener() == changeListener) {
|
||||
changeListeners.remove(s);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return changeListeners.remove(changeListener);
|
||||
}
|
||||
|
||||
static synchronized List<FlightRecorderListener> getListeners() {
|
||||
@ -167,7 +138,7 @@ public final class PlatformRecorder {
|
||||
|
||||
synchronized Timer getTimer() {
|
||||
if (timer == null) {
|
||||
timer = createTimer();
|
||||
timer = new Timer("JFR Recording Scheduler", true);
|
||||
}
|
||||
return timer;
|
||||
}
|
||||
@ -366,7 +337,7 @@ public final class PlatformRecorder {
|
||||
}
|
||||
|
||||
private Instant dumpMemoryToDestination(PlatformRecording recording) {
|
||||
WriteableUserPath dest = recording.getDestination();
|
||||
WriteablePath dest = recording.getDestination();
|
||||
if (dest != null) {
|
||||
Instant t = MetadataRepository.getInstance().setOutput(dest.getRealPathText());
|
||||
recording.clearDestination();
|
||||
@ -441,8 +412,8 @@ public final class PlatformRecorder {
|
||||
}
|
||||
|
||||
private void startDiskMonitor() {
|
||||
Thread t = SecuritySupport.createThreadWitNoPermissions("JFR Periodic Tasks", () -> periodicTask());
|
||||
SecuritySupport.setDaemonThread(t, true);
|
||||
Thread t = new Thread(() -> periodicTask(), "JFR Periodic Tasks");
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
@ -472,7 +443,7 @@ public final class PlatformRecorder {
|
||||
if (ActiveRecordingEvent.enabled()) {
|
||||
for (PlatformRecording r : getRecordings()) {
|
||||
if (r.getState() == RecordingState.RUNNING && r.shouldWriteMetadataEvent()) {
|
||||
WriteableUserPath path = r.getDestination();
|
||||
WriteablePath path = r.getDestination();
|
||||
Duration age = r.getMaxAge();
|
||||
Duration flush = r.getFlushInterval();
|
||||
Long size = r.getMaxSize();
|
||||
@ -519,7 +490,7 @@ public final class PlatformRecorder {
|
||||
wait = Math.min(minDelta, Options.getWaitInterval());
|
||||
} catch (Throwable t) {
|
||||
// Catch everything and log, but don't allow it to end the periodic task
|
||||
Logger.log(JFR_SYSTEM, ERROR, "Error in Periodic task: " + t.getClass().getName());
|
||||
Logger.log(JFR_SYSTEM, WARN, "Error in Periodic task: " + t.getMessage());
|
||||
} finally {
|
||||
takeNap(wait);
|
||||
}
|
||||
@ -660,7 +631,7 @@ public final class PlatformRecorder {
|
||||
target.setInternalDuration(startTime.until(endTime));
|
||||
}
|
||||
|
||||
public synchronized void migrate(SafePath repo) throws IOException {
|
||||
public synchronized void migrate(Path repo) throws IOException {
|
||||
// Must set repository while holding recorder lock so
|
||||
// the final chunk in repository gets marked correctly
|
||||
Repository.getRepository().setBasePath(repo);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -35,11 +35,8 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
@ -59,7 +56,6 @@ import jdk.jfr.Configuration;
|
||||
import jdk.jfr.FlightRecorderListener;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.RecordingState;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
import jdk.jfr.internal.util.ValueFormatter;
|
||||
|
||||
@ -73,12 +69,12 @@ public final class PlatformRecording implements AutoCloseable {
|
||||
private Duration maxAge;
|
||||
private long maxSize;
|
||||
|
||||
private WriteableUserPath destination;
|
||||
private WriteablePath destination;
|
||||
|
||||
private boolean toDisk = true;
|
||||
private String name;
|
||||
private boolean dumpOnExit;
|
||||
private SafePath dumpDirectory;
|
||||
private Path dumpDirectory;
|
||||
// Timestamp information
|
||||
private Instant stopTime;
|
||||
private Instant startTime;
|
||||
@ -90,8 +86,6 @@ public final class PlatformRecording implements AutoCloseable {
|
||||
private volatile Recording recording;
|
||||
private TimerTask stopTask;
|
||||
private TimerTask startTask;
|
||||
@SuppressWarnings("removal")
|
||||
private final AccessControlContext dumpDirectoryControlContext;
|
||||
private boolean shouldWriteActiveRecordingEvent = true;
|
||||
private Duration flushInterval = Duration.ofSeconds(1);
|
||||
private long finalStartChunkNanos = Long.MIN_VALUE;
|
||||
@ -99,13 +93,6 @@ public final class PlatformRecording implements AutoCloseable {
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
PlatformRecording(PlatformRecorder recorder, long id) {
|
||||
// Typically the access control context is taken
|
||||
// when you call dump(Path) or setDestination(Path),
|
||||
// but if no destination is set and the filename is auto-generated,
|
||||
// the control context of the recording is taken when the
|
||||
// Recording object is constructed. This works well for
|
||||
// -XX:StartFlightRecording and JFR.dump
|
||||
this.dumpDirectoryControlContext = AccessController.getContext();
|
||||
this.id = id;
|
||||
this.recorder = recorder;
|
||||
this.name = String.valueOf(id);
|
||||
@ -175,7 +162,7 @@ public final class PlatformRecording implements AutoCloseable {
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Stopped recording \"" + getName() + "\" (" + getId() + ")" + endText);
|
||||
newState = getState();
|
||||
}
|
||||
WriteableUserPath dest = getDestination();
|
||||
WriteablePath dest = getDestination();
|
||||
if (dest == null && dumpDirectory != null) {
|
||||
dest = makeDumpPath();
|
||||
}
|
||||
@ -195,30 +182,18 @@ public final class PlatformRecording implements AutoCloseable {
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public WriteableUserPath makeDumpPath() {
|
||||
public WriteablePath makeDumpPath() {
|
||||
try {
|
||||
String name = JVMSupport.makeFilename(getRecording());
|
||||
return AccessController.doPrivileged(new PrivilegedExceptionAction<WriteableUserPath>() {
|
||||
@Override
|
||||
public WriteableUserPath run() throws Exception {
|
||||
SafePath p = dumpDirectory;
|
||||
if (p == null) {
|
||||
p = new SafePath(".");
|
||||
}
|
||||
return new WriteableUserPath(p.toPath().resolve(name));
|
||||
}
|
||||
}, dumpDirectoryControlContext);
|
||||
} catch (PrivilegedActionException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof SecurityException) {
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Not allowed to create dump path for recording " + recording.getId() + " on exit.");
|
||||
Path p = dumpDirectory;
|
||||
if (p == null) {
|
||||
p = Path.of(".");
|
||||
}
|
||||
if (t instanceof IOException) {
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not dump " + recording.getId() + " on exit.");
|
||||
}
|
||||
return null;
|
||||
return new WriteablePath(p.resolve(name));
|
||||
} catch (IOException e) {
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not dump " + recording.getId() + " on exit. " + e.getMessage());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@ -422,14 +397,16 @@ public final class PlatformRecording implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
public void setDestination(WriteableUserPath userSuppliedPath) throws IOException {
|
||||
public void setDestination(WriteablePath destination) throws IOException {
|
||||
synchronized (recorder) {
|
||||
checkSetDestination(userSuppliedPath);
|
||||
this.destination = userSuppliedPath;
|
||||
checkSetDestination(destination);
|
||||
this.destination = destination;
|
||||
}
|
||||
}
|
||||
|
||||
public void checkSetDestination(WriteableUserPath userSuppliedPath) throws IOException {
|
||||
public void checkSetDestination(WriteablePath writeablePath) throws IOException {
|
||||
// The writeablePath argument is not checked. It's sufficient that an instance has
|
||||
// been created.
|
||||
synchronized (recorder) {
|
||||
if (Utils.isState(getState(), RecordingState.STOPPED, RecordingState.CLOSED)) {
|
||||
throw new IllegalStateException("Destination can't be set on a recording that has been stopped/closed");
|
||||
@ -437,7 +414,7 @@ public final class PlatformRecording implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
public WriteableUserPath getDestination() {
|
||||
public WriteablePath getDestination() {
|
||||
synchronized (recorder) {
|
||||
return destination;
|
||||
}
|
||||
@ -707,8 +684,7 @@ public final class PlatformRecording implements AutoCloseable {
|
||||
try {
|
||||
stop("End of duration reached");
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not stop recording.");
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, "Could not stop recording. " + t.getMessage());
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -737,37 +713,34 @@ public final class PlatformRecording implements AutoCloseable {
|
||||
}
|
||||
|
||||
// Dump running and stopped recordings
|
||||
public void dump(WriteableUserPath writeableUserPath) throws IOException {
|
||||
public void dump(WriteablePath writeablePath) throws IOException {
|
||||
synchronized (recorder) {
|
||||
try(PlatformRecording p = newSnapshotClone("Dumped by user", null)) {
|
||||
p.dumpStopped(writeableUserPath);
|
||||
p.dumpStopped(writeablePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpStopped(WriteableUserPath userPath) throws IOException {
|
||||
public void dumpStopped(WriteablePath path) throws IOException {
|
||||
synchronized (recorder) {
|
||||
transferChunksWithRetry(userPath);
|
||||
transferChunksWithRetry(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void transferChunksWithRetry(WriteableUserPath userPath) throws IOException {
|
||||
userPath.doPrivilegedIO(() -> {
|
||||
try {
|
||||
transferChunks(userPath);
|
||||
} catch (NoSuchFileException nsfe) {
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, "Missing chunkfile when writing recording \"" + name + "\" (" + id + ") to " + userPath.getRealPathText() + ".");
|
||||
// if one chunkfile was missing, its likely more are missing
|
||||
removeNonExistantPaths();
|
||||
// and try the transfer again
|
||||
transferChunks(userPath);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
private void transferChunksWithRetry(WriteablePath path) throws IOException {
|
||||
try {
|
||||
transferChunks(path);
|
||||
} catch (NoSuchFileException nsfe) {
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, "Missing chunkfile when writing recording \"" + name + "\" (" + id + ") to " + path.getRealPathText() + ".");
|
||||
// if one chunkfile was missing, its likely more are missing
|
||||
removeNonExistantPaths();
|
||||
// and try the transfer again
|
||||
transferChunks(path);
|
||||
}
|
||||
}
|
||||
|
||||
private void transferChunks(WriteableUserPath userPath) throws IOException {
|
||||
try (ChunksChannel cc = new ChunksChannel(chunks); FileChannel fc = FileChannel.open(userPath.getReal(), StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
|
||||
private void transferChunks(WriteablePath path) throws IOException {
|
||||
try (ChunksChannel cc = new ChunksChannel(chunks); FileChannel fc = FileChannel.open(path.getReal(), StandardOpenOption.WRITE, StandardOpenOption.APPEND)) {
|
||||
long bytes = cc.transferTo(fc);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Transferred " + bytes + " bytes from the disk repository");
|
||||
// No need to force if no data was transferred, which avoids IOException when device is /dev/null
|
||||
@ -859,7 +832,7 @@ public final class PlatformRecording implements AutoCloseable {
|
||||
* <p>
|
||||
* Only to be used by DCmdStart.
|
||||
*/
|
||||
public void setDumpDirectory(SafePath directory) {
|
||||
public void setDumpDirectory(Path directory) {
|
||||
this.dumpDirectory = directory;
|
||||
}
|
||||
|
||||
@ -913,7 +886,7 @@ public final class PlatformRecording implements AutoCloseable {
|
||||
|
||||
}
|
||||
|
||||
public void removePath(SafePath path) {
|
||||
public void removePath(Path path) {
|
||||
synchronized (recorder) {
|
||||
Iterator<RepositoryChunk> it = chunks.iterator();
|
||||
while (it.hasNext()) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,7 +25,6 @@
|
||||
|
||||
package jdk.jfr.internal;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@ -35,7 +34,6 @@ import jdk.jfr.EventSettings;
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.FlightRecorderPermission;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.SettingDescriptor;
|
||||
import jdk.jfr.ValueDescriptor;
|
||||
import jdk.jfr.internal.management.EventSettingsModifier;
|
||||
@ -60,7 +58,7 @@ public abstract class PrivateAccess {
|
||||
// Will trigger
|
||||
// FlightRecorderPermission.<clinit>
|
||||
// which will call PrivateAccess.setPrivateAccess
|
||||
new FlightRecorderPermission(SecuritySupport.REGISTER_EVENT);
|
||||
new FlightRecorderPermission("accessFlightRecorder");
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
@ -99,9 +97,6 @@ public abstract class PrivateAccess {
|
||||
|
||||
public abstract PlatformRecorder getPlatformRecorder();
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public abstract AccessControlContext getContext(SettingControl sc);
|
||||
|
||||
public abstract EventSettings newEventSettings(EventSettingsModifier esm);
|
||||
|
||||
public abstract boolean isVisible(EventType t);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,6 +26,7 @@
|
||||
package jdk.jfr.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.LocalDateTime;
|
||||
@ -33,20 +34,20 @@ import java.time.ZoneOffset;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.management.ChunkFilename;
|
||||
import jdk.jfr.internal.util.ValueFormatter;
|
||||
import jdk.jfr.internal.util.DirectoryCleaner;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
|
||||
public final class Repository {
|
||||
|
||||
private static final Path JAVA_IO_TMPDIR = Utils.getPathInProperty("java.io.tmpdir", null);
|
||||
private static final int MAX_REPO_CREATION_RETRIES = 1000;
|
||||
private static final Repository instance = new Repository();
|
||||
|
||||
private static final String JFR_REPOSITORY_LOCATION_PROPERTY = "jdk.jfr.repository";
|
||||
|
||||
private final Set<SafePath> cleanupDirectories = new HashSet<>();
|
||||
private SafePath baseLocation;
|
||||
private SafePath repository;
|
||||
private final Set<Path> cleanupDirectories = new HashSet<>();
|
||||
private Path baseLocation;
|
||||
private Path repository;
|
||||
private ChunkFilename chunkFilename;
|
||||
|
||||
private Repository() {
|
||||
@ -56,7 +57,7 @@ public final class Repository {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public synchronized void setBasePath(SafePath baseLocation) throws IOException {
|
||||
public synchronized void setBasePath(Path baseLocation) throws IOException {
|
||||
if(baseLocation.equals(this.baseLocation)) {
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Same base repository path " + baseLocation.toString() + " is set");
|
||||
return;
|
||||
@ -68,7 +69,7 @@ public final class Repository {
|
||||
try {
|
||||
// Remove so we don't "leak" repositories, if JFR is never started
|
||||
// and shutdown hook not added.
|
||||
SecuritySupport.delete(repository);
|
||||
Files.delete(repository);
|
||||
} catch (IOException ioe) {
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Could not delete disk repository " + repository);
|
||||
}
|
||||
@ -77,25 +78,25 @@ public final class Repository {
|
||||
|
||||
public synchronized void ensureRepository() throws IOException {
|
||||
if (baseLocation == null) {
|
||||
setBasePath(SecuritySupport.JAVA_IO_TMPDIR);
|
||||
setBasePath(JAVA_IO_TMPDIR);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized RepositoryChunk newChunk() {
|
||||
LocalDateTime timestamp = timestamp();
|
||||
try {
|
||||
if (!SecuritySupport.existDirectory(repository)) {
|
||||
if (!Files.exists(repository)) {
|
||||
this.repository = createRepository(baseLocation);
|
||||
JVM.setRepositoryLocation(repository.toString());
|
||||
SecuritySupport.setProperty(JFR_REPOSITORY_LOCATION_PROPERTY, repository.toString());
|
||||
System.setProperty(JFR_REPOSITORY_LOCATION_PROPERTY, repository.toString());
|
||||
cleanupDirectories.add(repository);
|
||||
chunkFilename = null;
|
||||
}
|
||||
if (chunkFilename == null) {
|
||||
chunkFilename = ChunkFilename.newPriviliged(repository.toPath());
|
||||
chunkFilename = new ChunkFilename(repository);
|
||||
}
|
||||
String filename = chunkFilename.next(timestamp);
|
||||
return new RepositoryChunk(new SafePath(filename));
|
||||
return new RepositoryChunk(Path.of(filename));
|
||||
} catch (Exception e) {
|
||||
String errorMsg = String.format("Could not create chunk in repository %s, %s: %s", repository, e.getClass(), e.getMessage());
|
||||
Logger.log(LogTag.JFR, LogLevel.ERROR, errorMsg);
|
||||
@ -113,16 +114,16 @@ public final class Repository {
|
||||
}
|
||||
}
|
||||
|
||||
private static SafePath createRepository(SafePath basePath) throws IOException {
|
||||
SafePath canonicalBaseRepositoryPath = createRealBasePath(basePath);
|
||||
SafePath f = null;
|
||||
private static Path createRepository(Path basePath) throws IOException {
|
||||
Path canonicalBaseRepositoryPath = createRealBasePath(basePath);
|
||||
Path f = null;
|
||||
|
||||
String basename = ValueFormatter.formatDateTime(timestamp()) + "_" + JVM.getPid();
|
||||
String name = basename;
|
||||
|
||||
int i = 0;
|
||||
for (; i < MAX_REPO_CREATION_RETRIES; i++) {
|
||||
f = new SafePath(canonicalBaseRepositoryPath.toPath().resolve(name));
|
||||
f = canonicalBaseRepositoryPath.resolve(name);
|
||||
if (tryToUseAsRepository(f)) {
|
||||
break;
|
||||
}
|
||||
@ -132,41 +133,36 @@ public final class Repository {
|
||||
if (i == MAX_REPO_CREATION_RETRIES) {
|
||||
throw new IOException("Unable to create JFR repository directory using base location (" + basePath + ")");
|
||||
}
|
||||
return SecuritySupport.toRealPath(f);
|
||||
return f.toRealPath();
|
||||
}
|
||||
|
||||
private static SafePath createRealBasePath(SafePath safePath) throws IOException {
|
||||
if (SecuritySupport.exists(safePath)) {
|
||||
if (!SecuritySupport.isWritable(safePath)) {
|
||||
throw new IOException("JFR repository directory (" + safePath.toString() + ") exists, but isn't writable");
|
||||
private static Path createRealBasePath(Path path) throws IOException {
|
||||
if (Files.exists(path)) {
|
||||
if (!Files.isWritable(path)) {
|
||||
throw new IOException("JFR repository directory (" + path.toString() + ") exists, but isn't writable");
|
||||
}
|
||||
return SecuritySupport.toRealPath(safePath);
|
||||
return path.toRealPath();
|
||||
}
|
||||
SafePath p = SecuritySupport.createDirectories(safePath);
|
||||
return SecuritySupport.toRealPath(p);
|
||||
return Files.createDirectories(path).toRealPath();
|
||||
}
|
||||
|
||||
private static boolean tryToUseAsRepository(final SafePath path) {
|
||||
Path parent = path.toPath().getParent();
|
||||
private static boolean tryToUseAsRepository(Path path) {
|
||||
Path parent = path.getParent();
|
||||
if (parent == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
try {
|
||||
SecuritySupport.createDirectories(path);
|
||||
} catch (Exception e) {
|
||||
// file already existed or some other problem occurred
|
||||
}
|
||||
if (!SecuritySupport.exists(path)) {
|
||||
return false;
|
||||
}
|
||||
if (!SecuritySupport.isDirectory(path)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (IOException io) {
|
||||
Files.createDirectories(path);
|
||||
} catch (Exception e) {
|
||||
// file already existed or some other problem occurred
|
||||
}
|
||||
if (!Files.exists(path)) {
|
||||
return false;
|
||||
}
|
||||
if (!Files.isDirectory(path)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
synchronized void clear() {
|
||||
@ -174,9 +170,9 @@ public final class Repository {
|
||||
return;
|
||||
}
|
||||
|
||||
for (SafePath p : cleanupDirectories) {
|
||||
for (Path p : cleanupDirectories) {
|
||||
try {
|
||||
SecuritySupport.clearDirectory(p);
|
||||
DirectoryCleaner.clear(p);
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Removed repository " + p);
|
||||
} catch (IOException e) {
|
||||
Logger.log(LogTag.JFR, LogLevel.INFO, "Repository " + p + " could not be removed at shutdown: " + e.getMessage());
|
||||
@ -184,11 +180,11 @@ public final class Repository {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized SafePath getRepositoryPath() {
|
||||
public synchronized Path getRepositoryPath() {
|
||||
return repository;
|
||||
}
|
||||
|
||||
public synchronized SafePath getBaseLocation() {
|
||||
public synchronized Path getBaseLocation() {
|
||||
return baseLocation;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,12 +27,14 @@ package jdk.jfr.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.time.Instant;
|
||||
import java.util.Comparator;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
|
||||
public final class RepositoryChunk {
|
||||
|
||||
static final Comparator<RepositoryChunk> END_TIME_COMPARATOR = new Comparator<RepositoryChunk>() {
|
||||
@ -42,7 +44,7 @@ public final class RepositoryChunk {
|
||||
}
|
||||
};
|
||||
|
||||
private final SafePath chunkFile;
|
||||
private final Path chunkFile;
|
||||
private final RandomAccessFile unFinishedRAF;
|
||||
|
||||
private Instant endTime = null; // unfinished
|
||||
@ -50,15 +52,15 @@ public final class RepositoryChunk {
|
||||
private int refCount = 1;
|
||||
private long size;
|
||||
|
||||
RepositoryChunk(SafePath path) throws Exception {
|
||||
RepositoryChunk(Path path) throws Exception {
|
||||
this.chunkFile = path;
|
||||
this.unFinishedRAF = SecuritySupport.createRandomAccessFile(chunkFile);
|
||||
this.unFinishedRAF = new RandomAccessFile(path.toFile(), "rw");
|
||||
}
|
||||
|
||||
boolean finish(Instant endTime) {
|
||||
try {
|
||||
unFinishedRAF.close();
|
||||
size = SecuritySupport.getFileSize(chunkFile);
|
||||
size = Files.size(chunkFile);
|
||||
this.endTime = endTime;
|
||||
if (Logger.shouldLog(LogTag.JFR_SYSTEM, LogLevel.DEBUG)) {
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, "Chunk finished: " + chunkFile);
|
||||
@ -89,9 +91,9 @@ public final class RepositoryChunk {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
private void delete(SafePath f) {
|
||||
private void delete(Path f) {
|
||||
try {
|
||||
SecuritySupport.delete(f);
|
||||
Files.delete(f);
|
||||
if (Logger.shouldLog(LogTag.JFR, LogLevel.DEBUG)) {
|
||||
Logger.log(LogTag.JFR, LogLevel.DEBUG, "Repository chunk " + f + " deleted");
|
||||
}
|
||||
@ -153,7 +155,7 @@ public final class RepositoryChunk {
|
||||
if (!isFinished()) {
|
||||
throw new IOException("Chunk not finished");
|
||||
}
|
||||
return ((SecuritySupport.newFileChannelToRead(chunkFile)));
|
||||
return FileChannel.open(chunkFile, StandardOpenOption.READ);
|
||||
}
|
||||
|
||||
public boolean inInterval(Instant startTime, Instant endTime) {
|
||||
@ -166,23 +168,19 @@ public final class RepositoryChunk {
|
||||
return true;
|
||||
}
|
||||
|
||||
public SafePath getFile() {
|
||||
public Path getFile() {
|
||||
return chunkFile;
|
||||
}
|
||||
|
||||
public long getCurrentFileSize() {
|
||||
try {
|
||||
return SecuritySupport.getFileSize(chunkFile);
|
||||
return Files.size(chunkFile);
|
||||
} catch (IOException e) {
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
boolean isMissingFile() {
|
||||
try {
|
||||
return !SecuritySupport.exists(chunkFile);
|
||||
} catch (IOException ioe) {
|
||||
return true;
|
||||
}
|
||||
return !Files.exists(chunkFile);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,63 +25,13 @@
|
||||
|
||||
package jdk.jfr.internal;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.io.Reader;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.ReflectPermission;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.Permission;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.PropertyPermission;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import jdk.internal.module.Modules;
|
||||
import jdk.jfr.Event;
|
||||
import jdk.jfr.FlightRecorder;
|
||||
import jdk.jfr.FlightRecorderListener;
|
||||
import jdk.jfr.FlightRecorderPermission;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.internal.consumer.FileAccess;
|
||||
|
||||
/**
|
||||
* Contains JFR code that does
|
||||
* {@link AccessController#doPrivileged(PrivilegedAction)}
|
||||
*/
|
||||
public final class SecuritySupport {
|
||||
private static final String EVENTS_PACKAGE_NAME = "jdk.jfr.events";
|
||||
private static final String EVENT_PACKAGE_NAME = "jdk.jfr.internal.event";
|
||||
|
||||
public static final String REGISTER_EVENT = "registerEvent";
|
||||
public static final String ACCESS_FLIGHT_RECORDER = "accessFlightRecorder";
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
private static final Module JFR_MODULE = Event.class.getModule();
|
||||
public static final SafePath JFC_DIRECTORY = getPathInProperty("java.home", "lib/jfr");
|
||||
public static final FileAccess PRIVILEGED = new Privileged();
|
||||
static final SafePath JAVA_IO_TMPDIR = getPathInProperty("java.io.tmpdir", null);
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
static {
|
||||
// ensure module java.base can read module jdk.jfr as early as possible
|
||||
@ -90,215 +40,6 @@ public final class SecuritySupport {
|
||||
addEventsExport(Object.class);
|
||||
}
|
||||
|
||||
static final class SecureRecorderListener implements FlightRecorderListener {
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private final AccessControlContext context;
|
||||
private final FlightRecorderListener changeListener;
|
||||
|
||||
SecureRecorderListener(@SuppressWarnings("removal") AccessControlContext context, FlightRecorderListener changeListener) {
|
||||
this.context = Objects.requireNonNull(context);
|
||||
this.changeListener = Objects.requireNonNull(changeListener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
@Override
|
||||
public void recordingStateChanged(Recording recording) {
|
||||
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||
try {
|
||||
changeListener.recordingStateChanged(recording);
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Unexpected exception in listener " + changeListener.getClass()+ " at recording state change");
|
||||
}
|
||||
return null;
|
||||
}, context);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
@Override
|
||||
public void recorderInitialized(FlightRecorder recorder) {
|
||||
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||
try {
|
||||
changeListener.recorderInitialized(recorder);
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Unexpected exception in listener " + changeListener.getClass()+ " when initializing FlightRecorder");
|
||||
}
|
||||
return null;
|
||||
}, context);
|
||||
}
|
||||
|
||||
public FlightRecorderListener getChangeListener() {
|
||||
return changeListener;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DirectoryCleaner extends SimpleFileVisitor<Path> {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(path);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
if (exc != null) {
|
||||
throw exc;
|
||||
}
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Path created by the default file provider, and not
|
||||
* a malicious provider.
|
||||
*
|
||||
*/
|
||||
public static final class SafePath implements Comparable<SafePath> {
|
||||
private final Path path;
|
||||
private final String text;
|
||||
|
||||
public SafePath(Path p) {
|
||||
// sanitize
|
||||
text = p.toString();
|
||||
path = Paths.get(text);
|
||||
}
|
||||
|
||||
public SafePath(String path) {
|
||||
this(Paths.get(path));
|
||||
}
|
||||
|
||||
public Path toPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public File toFile() {
|
||||
return path.toFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return text;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(SafePath that) {
|
||||
return that.text.compareTo(this.text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if(other != null && other instanceof SafePath s){
|
||||
return this.toPath().equals(s.toPath());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.toPath().hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
private interface RunnableWithCheckedException {
|
||||
public void run() throws Exception;
|
||||
}
|
||||
|
||||
private interface CallableWithoutCheckException<T> {
|
||||
public T call();
|
||||
}
|
||||
|
||||
public static void checkAccessFlightRecorder() throws SecurityException {
|
||||
@SuppressWarnings("removal")
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(new FlightRecorderPermission(ACCESS_FLIGHT_RECORDER));
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkRegisterPermission() throws SecurityException {
|
||||
@SuppressWarnings("removal")
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
sm.checkPermission(new FlightRecorderPermission(REGISTER_EVENT));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private static <U> U doPrivilegedIOWithReturn(Callable<U> function) throws IOException {
|
||||
try {
|
||||
return AccessController.doPrivileged(new PrivilegedExceptionAction<U>() {
|
||||
@Override
|
||||
public U run() throws Exception {
|
||||
return function.call();
|
||||
}
|
||||
}, null);
|
||||
} catch (PrivilegedActionException e) {
|
||||
Throwable t = e.getCause();
|
||||
if (t instanceof IOException) {
|
||||
throw (IOException) t;
|
||||
}
|
||||
throw new IOException("Unexpected error during I/O operation. " + t.getMessage(), t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void doPriviligedIO(RunnableWithCheckedException function) throws IOException {
|
||||
doPrivilegedIOWithReturn(() -> {
|
||||
function.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private static void doPrivileged(Runnable function, Permission... perms) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
function.run();
|
||||
return null;
|
||||
}
|
||||
}, null, perms);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private static void doPrivileged(Runnable function) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
function.run();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private static <T> T doPrivilegedWithReturn(CallableWithoutCheckException<T> function, Permission... perms) {
|
||||
return AccessController.doPrivileged(new PrivilegedAction<T>() {
|
||||
@Override
|
||||
public T run() {
|
||||
return function.call();
|
||||
}
|
||||
}, null, perms);
|
||||
}
|
||||
|
||||
public static List<SafePath> getPredefinedJFCFiles() {
|
||||
List<SafePath> list = new ArrayList<>();
|
||||
try (var ds = doPrivilegedIOWithReturn(() -> Files.newDirectoryStream(JFC_DIRECTORY.toPath()))) {
|
||||
for (Path path : ds) {
|
||||
SafePath s = new SafePath(path);
|
||||
String text = s.toString();
|
||||
if (text.endsWith(".jfc") && !SecuritySupport.isDirectory(s)) {
|
||||
list.add(s);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not access .jfc-files in " + JFC_DIRECTORY + ", " + ioe.getMessage());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static void makeVisibleToJFR(Class<?> clazz) {
|
||||
Module classModule = clazz.getModule();
|
||||
Modules.addReads(JFR_MODULE, classModule);
|
||||
@ -314,213 +55,31 @@ public final class SecuritySupport {
|
||||
* (for EventConfiguration and EventWriter)
|
||||
*/
|
||||
static void addInternalEventExport(Class<?> clazz) {
|
||||
Modules.addExports(JFR_MODULE, EVENT_PACKAGE_NAME, clazz.getModule());
|
||||
Modules.addExports(JFR_MODULE, "jdk.jfr.internal.event", clazz.getModule());
|
||||
}
|
||||
|
||||
static void addEventsExport(Class<?> clazz) {
|
||||
Modules.addExports(JFR_MODULE, EVENTS_PACKAGE_NAME, clazz.getModule());
|
||||
Modules.addExports(JFR_MODULE, "jdk.jfr.events", clazz.getModule());
|
||||
}
|
||||
|
||||
static void addReadEdge(Class<?> clazz) {
|
||||
Modules.addReads(clazz.getModule(), JFR_MODULE);
|
||||
}
|
||||
|
||||
public static void registerEvent(Class<? extends jdk.internal.event.Event> eventClass) {
|
||||
doPrivileged(() -> MetadataRepository.getInstance().register(eventClass), new FlightRecorderPermission(REGISTER_EVENT));
|
||||
}
|
||||
|
||||
public static void setProperty(String propertyName, String value) {
|
||||
doPrivileged(() -> System.setProperty(propertyName, value), new PropertyPermission(propertyName, "write"));
|
||||
}
|
||||
|
||||
static boolean getBooleanProperty(String propertyName) {
|
||||
return doPrivilegedWithReturn(() -> Boolean.getBoolean(propertyName), new PropertyPermission(propertyName, "read"));
|
||||
}
|
||||
|
||||
private static SafePath getPathInProperty(String prop, String subPath) {
|
||||
return doPrivilegedWithReturn(() -> {
|
||||
String path = System.getProperty(prop);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
File file = subPath == null ? new File(path) : new File(path, subPath);
|
||||
return new SafePath(file.getAbsolutePath());
|
||||
}, new PropertyPermission("*", "read"));
|
||||
}
|
||||
|
||||
// Called by JVM during initialization of JFR
|
||||
static Thread createRecorderThread(ThreadGroup systemThreadGroup, ClassLoader contextClassLoader) {
|
||||
// The thread should have permission = new Permission[0], and not "modifyThreadGroup" and "modifyThread" on the stack,
|
||||
// but it's hard circumvent if we are going to pass in system thread group in the constructor
|
||||
Thread thread = doPrivilegedWithReturn(() -> new Thread(systemThreadGroup, "JFR Recorder Thread"), new RuntimePermission("modifyThreadGroup"), new RuntimePermission("modifyThread"));
|
||||
doPrivileged(() -> thread.setContextClassLoader(contextClassLoader), new RuntimePermission("setContextClassLoader"), new RuntimePermission("modifyThread"));
|
||||
return thread;
|
||||
}
|
||||
|
||||
static void registerShutdownHook(Thread shutdownHook) {
|
||||
doPrivileged(() -> Runtime.getRuntime().addShutdownHook(shutdownHook), new RuntimePermission("shutdownHooks"));
|
||||
}
|
||||
|
||||
static void setUncaughtExceptionHandler(Thread thread, Thread.UncaughtExceptionHandler eh) {
|
||||
doPrivileged(() -> thread.setUncaughtExceptionHandler(eh), new RuntimePermission("modifyThread"));
|
||||
}
|
||||
|
||||
static void clearDirectory(SafePath safePath) throws IOException {
|
||||
doPriviligedIO(() -> Files.walkFileTree(safePath.toPath(), new DirectoryCleaner()));
|
||||
}
|
||||
|
||||
static SafePath toRealPath(SafePath safePath, LinkOption... options) throws IOException {
|
||||
return new SafePath(doPrivilegedIOWithReturn(() -> safePath.toPath().toRealPath(options)));
|
||||
}
|
||||
|
||||
static boolean existDirectory(SafePath directory) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.exists(directory.toPath()));
|
||||
}
|
||||
|
||||
static RandomAccessFile createRandomAccessFile(SafePath path) throws Exception {
|
||||
return doPrivilegedIOWithReturn(() -> new RandomAccessFile(path.toPath().toFile(), "rw"));
|
||||
}
|
||||
|
||||
public static InputStream newFileInputStream(SafePath safePath) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.newInputStream(safePath.toPath()));
|
||||
}
|
||||
|
||||
public static long getFileSize(SafePath safePath) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.size(safePath.toPath()));
|
||||
}
|
||||
|
||||
static SafePath createDirectories(SafePath safePath) throws IOException {
|
||||
Path p = doPrivilegedIOWithReturn(() -> Files.createDirectories(safePath.toPath()));
|
||||
return new SafePath(p);
|
||||
}
|
||||
|
||||
public static boolean exists(SafePath safePath) throws IOException {
|
||||
// Files.exist(path) is allocation intensive
|
||||
return doPrivilegedIOWithReturn(() -> safePath.toPath().toFile().exists());
|
||||
}
|
||||
|
||||
public static boolean isDirectory(SafePath safePath) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.isDirectory(safePath.toPath()));
|
||||
}
|
||||
|
||||
static void delete(SafePath localPath) throws IOException {
|
||||
doPriviligedIO(() -> Files.delete(localPath.toPath()));
|
||||
}
|
||||
|
||||
static boolean isWritable(SafePath safePath) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.isWritable(safePath.toPath()));
|
||||
}
|
||||
|
||||
static ReadableByteChannel newFileChannelToRead(SafePath safePath) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> FileChannel.open(safePath.toPath(), StandardOpenOption.READ));
|
||||
}
|
||||
|
||||
public static InputStream getResourceAsStream(String name) throws IOException {
|
||||
return doPrivilegedIOWithReturn(() -> SecuritySupport.class.getResourceAsStream(name));
|
||||
}
|
||||
|
||||
public static Reader newFileReader(SafePath safePath) throws FileNotFoundException, IOException {
|
||||
return doPrivilegedIOWithReturn(() -> Files.newBufferedReader(safePath.toPath()));
|
||||
}
|
||||
|
||||
static void setAccessible(Method method) {
|
||||
doPrivileged(() -> method.setAccessible(true), new ReflectPermission("suppressAccessChecks"));
|
||||
}
|
||||
|
||||
static void setAccessible(Constructor<?> constructor) {
|
||||
doPrivileged(() -> constructor.setAccessible(true), new ReflectPermission("suppressAccessChecks"));
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public static void ensureClassIsInitialized(Class<?> clazz) {
|
||||
try {
|
||||
MethodHandles.Lookup lookup;
|
||||
if (System.getSecurityManager() == null) {
|
||||
lookup = MethodHandles.privateLookupIn(clazz, LOOKUP);
|
||||
} else {
|
||||
lookup = AccessController.doPrivileged(new PrivilegedExceptionAction<>() {
|
||||
@Override
|
||||
public MethodHandles.Lookup run() throws IllegalAccessException {
|
||||
return MethodHandles.privateLookupIn(clazz, LOOKUP);
|
||||
}
|
||||
}, null, new ReflectPermission("suppressAccessChecks"));
|
||||
}
|
||||
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(clazz, LOOKUP);
|
||||
lookup.ensureInitialized(clazz);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalError(e);
|
||||
} catch (PrivilegedActionException e) {
|
||||
throw new InternalError(e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
static Class<?> defineClass(Class<?> lookupClass, byte[] bytes) {
|
||||
return AccessController.doPrivileged(new PrivilegedAction<Class<?>>() {
|
||||
@Override
|
||||
public Class<?> run() {
|
||||
try {
|
||||
return MethodHandles.privateLookupIn(lookupClass, LOOKUP).defineClass(bytes);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Thread createThreadWitNoPermissions(String threadName, Runnable runnable) {
|
||||
return doPrivilegedWithReturn(() -> new Thread(runnable, threadName), new Permission[0]);
|
||||
}
|
||||
|
||||
public static void setDaemonThread(Thread t, boolean daemon) {
|
||||
doPrivileged(()-> t.setDaemon(daemon), new RuntimePermission("modifyThread"));
|
||||
}
|
||||
|
||||
public static SafePath getAbsolutePath(SafePath path) throws IOException {
|
||||
return new SafePath(doPrivilegedIOWithReturn((()-> path.toPath().toAbsolutePath())));
|
||||
}
|
||||
|
||||
private static final class Privileged extends FileAccess {
|
||||
@Override
|
||||
public RandomAccessFile openRAF(File f, String mode) throws IOException {
|
||||
return doPrivilegedIOWithReturn( () -> new RandomAccessFile(f, mode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> newDirectoryStream(Path directory) throws IOException {
|
||||
return doPrivilegedIOWithReturn( () -> Files.newDirectoryStream(directory));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAbsolutePath(File f) throws IOException {
|
||||
return doPrivilegedIOWithReturn( () -> f.getAbsolutePath());
|
||||
}
|
||||
@Override
|
||||
public long length(File f) throws IOException {
|
||||
return doPrivilegedIOWithReturn( () -> f.length());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long fileSize(Path p) throws IOException {
|
||||
return doPrivilegedIOWithReturn( () -> Files.size(p));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(Path p) throws IOException {
|
||||
return doPrivilegedIOWithReturn( () -> Files.exists(p));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory(Path p) {
|
||||
return doPrivilegedWithReturn( () -> Files.isDirectory(p));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime getLastModified(Path p) throws IOException {
|
||||
// Timestamp only needed when examining repository for other JVMs,
|
||||
// in which case an unprivileged mode should be used.
|
||||
throw new InternalError("Should not reach here");
|
||||
try {
|
||||
return MethodHandles.privateLookupIn(lookupClass, LOOKUP).defineClass(bytes);
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,22 +25,18 @@
|
||||
|
||||
package jdk.jfr.internal;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
|
||||
import jdk.jfr.RecordingState;
|
||||
|
||||
/**
|
||||
* Class responsible for dumping recordings on exit
|
||||
*
|
||||
*/
|
||||
final class ShutdownHook implements Runnable {
|
||||
final class ShutdownHook extends Thread {
|
||||
private final PlatformRecorder recorder;
|
||||
Object tlabDummyObject;
|
||||
|
||||
ShutdownHook(PlatformRecorder recorder) {
|
||||
super("JFR Shutdown Hook");
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
@ -61,7 +57,7 @@ final class ShutdownHook implements Runnable {
|
||||
|
||||
private void dump(PlatformRecording recording) {
|
||||
try {
|
||||
WriteableUserPath dest = recording.getDestination();
|
||||
WriteablePath dest = recording.getDestination();
|
||||
if (dest == null) {
|
||||
dest = recording.makeDumpPath();
|
||||
recording.setDestination(dest);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -189,7 +189,7 @@ public final class TypeLibrary {
|
||||
Modules.addExports(proxyModule, proxyPackage, jfrModule);
|
||||
}
|
||||
}
|
||||
SecuritySupport.setAccessible(m);
|
||||
m.setAccessible(true);
|
||||
try {
|
||||
return m.invoke(annotation, new Object[0]);
|
||||
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -22,34 +22,42 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jfr.internal.periodic;
|
||||
|
||||
import jdk.internal.event.Event;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
package jdk.jfr.internal;
|
||||
|
||||
/**
|
||||
* Periodic task that runs trusted code that doesn't require an access control
|
||||
* context.
|
||||
* <p>
|
||||
* This class can be removed once the Security Manager is no longer supported.
|
||||
*/
|
||||
final class JDKEventTask extends JavaEventTask {
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public JDKEventTask(Class<? extends Event> eventClass, Runnable runnable) {
|
||||
super(eventClass, runnable);
|
||||
if (!getEventType().isJDK()) {
|
||||
throw new InternalError("Must be a JDK event");
|
||||
}
|
||||
if (!Utils.isJDKClass(eventClass)) {
|
||||
throw new SecurityException("Periodic task can only be registered for event classes that belongs to the JDK");
|
||||
}
|
||||
if (!Utils.isJDKClass(runnable.getClass())) {
|
||||
throw new SecurityException("Runnable class must belong to the JDK");
|
||||
public final class WriteablePath {
|
||||
private final Path path;
|
||||
private final Path real;
|
||||
|
||||
public WriteablePath(Path path) throws IOException {
|
||||
// verify that the path is writeable
|
||||
if (Files.exists(path) && !Files.isWritable(path)) {
|
||||
// throw same type of exception as FileOutputStream
|
||||
// constructor, if file can't be opened.
|
||||
throw new FileNotFoundException("Could not write to file: " + path.toAbsolutePath());
|
||||
}
|
||||
// will throw if non-writeable
|
||||
BufferedWriter fw = Files.newBufferedWriter(path);
|
||||
fw.close();
|
||||
this.path = path;
|
||||
this.real = path.toRealPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(long timestamp, PeriodicType periodicType) {
|
||||
getRunnable().run();
|
||||
public Path getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public Path getReal() {
|
||||
return real;
|
||||
}
|
||||
|
||||
public String getRealPathText() {
|
||||
return real.toString();
|
||||
}
|
||||
}
|
||||
@ -1,148 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.jfr.internal;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* Purpose of this class is to simplify analysis of security risks.
|
||||
* <p>
|
||||
* Paths in the public API should be wrapped in this class so we
|
||||
* at all time know what kind of paths we are dealing with.
|
||||
* <p>
|
||||
* A user supplied path must never be used in an unsafe context, such as a
|
||||
* shutdown hook or any other thread created by JFR.
|
||||
* <p>
|
||||
* All operation using this path must happen in {@link #doPrivilegedIO(Callable)}
|
||||
*/
|
||||
public final class WriteableUserPath {
|
||||
@SuppressWarnings("removal")
|
||||
private final AccessControlContext controlContext;
|
||||
private final Path original;
|
||||
private final Path real;
|
||||
private final String realPathText;
|
||||
private final String originalText;
|
||||
|
||||
// Not to ensure security, but to help
|
||||
// against programming errors
|
||||
private volatile boolean inPrivileged;
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public WriteableUserPath(Path path) throws IOException {
|
||||
controlContext = AccessController.getContext();
|
||||
// verify that the path is writeable
|
||||
if (Files.exists(path) && !Files.isWritable(path)) {
|
||||
// throw same type of exception as FileOutputStream
|
||||
// constructor, if file can't be opened.
|
||||
throw new FileNotFoundException("Could not write to file: " + path.toAbsolutePath());
|
||||
}
|
||||
// will throw if non-writeable
|
||||
BufferedWriter fw = Files.newBufferedWriter(path);
|
||||
fw.close();
|
||||
this.original = path;
|
||||
this.originalText = path.toString();
|
||||
this.real = path.toRealPath();
|
||||
this.realPathText = real.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a potentially malicious path where the user may have implemented
|
||||
* their own version of Path. This method should never be called in an
|
||||
* unsafe context and the Path value should never be passed along to other
|
||||
* methods.
|
||||
*
|
||||
* @return path from a potentially malicious user
|
||||
*/
|
||||
public Path getPotentiallyMaliciousOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the real path.
|
||||
*
|
||||
* @return path as text
|
||||
*/
|
||||
public String getRealPathText() {
|
||||
return realPathText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the original path.
|
||||
*
|
||||
* @return path as text
|
||||
*/
|
||||
public String getOriginalText() {
|
||||
return originalText;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a potentially malicious path where the user may have implemented
|
||||
* their own version of Path. This method should never be called in an
|
||||
* unsafe context and the Path value should never be passed along to other
|
||||
* methods.
|
||||
*
|
||||
* @return path from a potentially malicious user
|
||||
*/
|
||||
public Path getReal() {
|
||||
if (!inPrivileged) {
|
||||
throw new InternalError("A user path was accessed outside the context it was supplied in");
|
||||
}
|
||||
return real;
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public void doPrivilegedIO(Callable<?> function) throws IOException {
|
||||
try {
|
||||
inPrivileged = true;
|
||||
AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
|
||||
@Override
|
||||
public Void run() throws Exception {
|
||||
function.call();
|
||||
return null;
|
||||
}
|
||||
}, controlContext);
|
||||
} catch (Throwable t) {
|
||||
// prevent malicious user to propagate exception callback
|
||||
// in the wrong context
|
||||
Throwable cause = null;
|
||||
if (System.getSecurityManager() == null) {
|
||||
cause = t;
|
||||
}
|
||||
throw new IOException("Unexpected error during I/O operation", cause);
|
||||
} finally {
|
||||
inPrivileged = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,9 +26,6 @@
|
||||
package jdk.jfr.internal.consumer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
@ -46,7 +43,6 @@ import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
|
||||
/*
|
||||
* Purpose of this class is to simplify the implementation of
|
||||
@ -57,8 +53,6 @@ public abstract class AbstractEventStream implements EventStream {
|
||||
|
||||
private final CountDownLatch terminated = new CountDownLatch(1);
|
||||
private final Runnable flushOperation = () -> dispatcher().runFlushActions();
|
||||
@SuppressWarnings("removal")
|
||||
private final AccessControlContext accessControllerContext;
|
||||
private final StreamConfiguration streamConfiguration = new StreamConfiguration();
|
||||
private final List<Configuration> configurations;
|
||||
private final ParserState parserState = new ParserState();
|
||||
@ -67,8 +61,7 @@ public abstract class AbstractEventStream implements EventStream {
|
||||
private boolean daemon = false;
|
||||
|
||||
|
||||
AbstractEventStream(@SuppressWarnings("removal") AccessControlContext acc, List<Configuration> configurations) throws IOException {
|
||||
this.accessControllerContext = Objects.requireNonNull(acc);
|
||||
AbstractEventStream(List<Configuration> configurations) throws IOException {
|
||||
this.configurations = configurations;
|
||||
}
|
||||
|
||||
@ -221,22 +214,21 @@ public abstract class AbstractEventStream implements EventStream {
|
||||
|
||||
public final void startAsync(long startNanos) {
|
||||
startInternal(startNanos);
|
||||
Runnable r = () -> run(accessControllerContext);
|
||||
Thread thread = SecuritySupport.createThreadWitNoPermissions(nextThreadName(), r);
|
||||
SecuritySupport.setDaemonThread(thread, daemon);
|
||||
Runnable r = () -> execute();
|
||||
Thread thread = new Thread(r, nextThreadName());
|
||||
thread.setDaemon(daemon);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public final void start(long startNanos) {
|
||||
startInternal(startNanos);
|
||||
run(accessControllerContext);
|
||||
execute();
|
||||
}
|
||||
|
||||
protected final Runnable getFlushOperation() {
|
||||
return flushOperation;
|
||||
}
|
||||
|
||||
|
||||
protected final void onFlush() {
|
||||
Runnable r = getFlushOperation();
|
||||
if (r != null) {
|
||||
@ -276,17 +268,6 @@ public abstract class AbstractEventStream implements EventStream {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private void run(AccessControlContext accessControlContext) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
execute();
|
||||
return null;
|
||||
}
|
||||
}, accessControlContext);
|
||||
}
|
||||
|
||||
private String nextThreadName() {
|
||||
return "JFR Event Stream " + counter.incrementAndGet();
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,12 +27,10 @@ package jdk.jfr.internal.consumer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessControlContext;
|
||||
import java.time.Instant;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@ -44,7 +42,6 @@ import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.PlatformRecording;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
import jdk.jfr.internal.management.StreamBarrier;
|
||||
|
||||
@ -58,7 +55,6 @@ public final class EventDirectoryStream extends AbstractEventStream {
|
||||
private static final Comparator<? super RecordedEvent> EVENT_COMPARATOR = JdkJfrConsumer.instance().eventComparator();
|
||||
|
||||
private final RepositoryFiles repositoryFiles;
|
||||
private final FileAccess fileAccess;
|
||||
private final PlatformRecording recording;
|
||||
private final StreamBarrier barrier = new StreamBarrier();
|
||||
private final AtomicLong streamId = new AtomicLong();
|
||||
@ -69,20 +65,13 @@ public final class EventDirectoryStream extends AbstractEventStream {
|
||||
private volatile Consumer<Long> onCompleteHandler;
|
||||
|
||||
public EventDirectoryStream(
|
||||
@SuppressWarnings("removal")
|
||||
AccessControlContext acc,
|
||||
Path p,
|
||||
FileAccess fileAccess,
|
||||
PlatformRecording recording,
|
||||
List<Configuration> configurations,
|
||||
boolean allowSubDirectories) throws IOException {
|
||||
super(acc, configurations);
|
||||
super(configurations);
|
||||
this.recording = recording;
|
||||
if (p != null && SecuritySupport.PRIVILEGED == fileAccess) {
|
||||
throw new SecurityException("Priviliged file access not allowed with potentially malicious Path implementation");
|
||||
}
|
||||
this.fileAccess = Objects.requireNonNull(fileAccess);
|
||||
this.repositoryFiles = new RepositoryFiles(fileAccess, p, allowSubDirectories);
|
||||
this.repositoryFiles = new RepositoryFiles(p, allowSubDirectories);
|
||||
this.streamId.incrementAndGet();
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Stream " + streamId + " started.");
|
||||
}
|
||||
@ -153,7 +142,7 @@ public final class EventDirectoryStream extends AbstractEventStream {
|
||||
return;
|
||||
}
|
||||
currentChunkStartNanos = repositoryFiles.getTimestamp(path);
|
||||
try (RecordingInput input = new RecordingInput(path.toFile(), fileAccess)) {
|
||||
try (RecordingInput input = new RecordingInput(path.toFile())) {
|
||||
input.setStreamed();
|
||||
currentParser = new ChunkParser(input, disp.parserConfiguration, parserState());
|
||||
long segmentStart = currentParser.getStartNanos() + currentParser.getChunkDuration();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,7 +27,6 @@ package jdk.jfr.internal.consumer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessControlContext;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@ -45,9 +44,9 @@ public final class EventFileStream extends AbstractEventStream {
|
||||
private ChunkParser currentParser;
|
||||
private RecordedEvent[] cacheSorted;
|
||||
|
||||
public EventFileStream(@SuppressWarnings("removal") AccessControlContext acc, Path file) throws IOException {
|
||||
super(acc, Collections.emptyList());
|
||||
this.input = new RecordingInput(file.toFile(), FileAccess.UNPRIVILEGED);
|
||||
public EventFileStream(Path file) throws IOException {
|
||||
super(Collections.emptyList());
|
||||
this.input = new RecordingInput(file.toFile());
|
||||
this.input.setStreamed();
|
||||
}
|
||||
|
||||
|
||||
@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.jfr.internal.consumer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
|
||||
// Protected by modular boundaries.
|
||||
public abstract class FileAccess {
|
||||
public static final FileAccess UNPRIVILEGED = new UnPrivileged();
|
||||
|
||||
public abstract RandomAccessFile openRAF(File f, String mode) throws IOException;
|
||||
|
||||
public abstract DirectoryStream<Path> newDirectoryStream(Path repository) throws IOException;
|
||||
|
||||
public abstract String getAbsolutePath(File f) throws IOException;
|
||||
|
||||
public abstract long length(File f) throws IOException;
|
||||
|
||||
public abstract long fileSize(Path p) throws IOException;
|
||||
|
||||
public abstract boolean exists(Path s) throws IOException;
|
||||
|
||||
public abstract boolean isDirectory(Path p);
|
||||
|
||||
public abstract FileTime getLastModified(Path p) throws IOException;
|
||||
|
||||
private static class UnPrivileged extends FileAccess {
|
||||
@Override
|
||||
public RandomAccessFile openRAF(File f, String mode) throws IOException {
|
||||
return new RandomAccessFile(f, mode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DirectoryStream<Path> newDirectoryStream(Path dir) throws IOException {
|
||||
return Files.newDirectoryStream(dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAbsolutePath(File f) throws IOException {
|
||||
return f.getAbsolutePath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long length(File f) throws IOException {
|
||||
return f.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long fileSize(Path p) throws IOException {
|
||||
return Files.size(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists(Path p) {
|
||||
return Files.exists(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory(Path p) {
|
||||
return Files.isDirectory(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileTime getLastModified(Path p) throws IOException {
|
||||
return Files.getLastModifiedTime(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,12 +26,12 @@ package jdk.jfr.internal.consumer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.RecordingState;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.management.EventByteStream;
|
||||
import jdk.jfr.internal.management.HiddenWait;
|
||||
import jdk.jfr.internal.management.ManagementSupport;
|
||||
@ -63,7 +63,7 @@ public final class OngoingStream extends EventByteStream {
|
||||
this.blockSize = blockSize;
|
||||
this.startTimeNanos = startTimeNanos;
|
||||
this.endTimeNanos = endTimeNanos;
|
||||
this.repositoryFiles = new RepositoryFiles(SecuritySupport.PRIVILEGED, null, false);
|
||||
this.repositoryFiles = new RepositoryFiles(null, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -206,10 +206,10 @@ public final class OngoingStream extends EventByteStream {
|
||||
|
||||
private boolean ensureInput() throws IOException {
|
||||
if (input == null) {
|
||||
if (SecuritySupport.getFileSize(new SafePath(path)) < HEADER_SIZE) {
|
||||
if (Files.size(path) < HEADER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
input = new RecordingInput(path.toFile(), SecuritySupport.PRIVILEGED);
|
||||
input = new RecordingInput(path.toFile());
|
||||
input.setStreamed();
|
||||
header = new ChunkHeader(input);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -33,7 +33,6 @@ import java.io.RandomAccessFile;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import jdk.jfr.internal.management.HiddenWait;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
|
||||
public final class RecordingInput implements DataInput, AutoCloseable {
|
||||
|
||||
@ -68,7 +67,6 @@ public final class RecordingInput implements DataInput, AutoCloseable {
|
||||
}
|
||||
}
|
||||
private final int blockSize;
|
||||
private final FileAccess fileAccess;
|
||||
private final HiddenWait threadSleeper = new HiddenWait();
|
||||
private long pollCount = 1000;
|
||||
private RandomAccessFile file;
|
||||
@ -79,26 +77,25 @@ public final class RecordingInput implements DataInput, AutoCloseable {
|
||||
private long size = -1; // Fail fast if setSize(...) has not been called
|
||||
// before parsing
|
||||
|
||||
RecordingInput(File f, FileAccess fileAccess, int blockSize) throws IOException {
|
||||
RecordingInput(File f, int blockSize) throws IOException {
|
||||
this.blockSize = blockSize;
|
||||
this.fileAccess = fileAccess;
|
||||
initialize(f);
|
||||
}
|
||||
|
||||
private void initialize(File f) throws IOException {
|
||||
this.filename = fileAccess.getAbsolutePath(f);
|
||||
this.file = fileAccess.openRAF(f, "r");
|
||||
this.filename = f.getAbsolutePath();
|
||||
this.file = new RandomAccessFile(f, "r");
|
||||
this.position = 0;
|
||||
this.size = -1;
|
||||
this.currentBlock.reset();
|
||||
previousBlock.reset();
|
||||
if (fileAccess.length(f) < 8) {
|
||||
throw new IOException("Not a valid Flight Recorder file. File length is only " + fileAccess.length(f) + " bytes.");
|
||||
if (f.length() < 8) {
|
||||
throw new IOException("Not a valid Flight Recorder file. File length is only " + f.length() + " bytes.");
|
||||
}
|
||||
}
|
||||
|
||||
public RecordingInput(File f, FileAccess fileAccess) throws IOException {
|
||||
this(f, fileAccess, DEFAULT_BLOCK_SIZE);
|
||||
public RecordingInput(File f) throws IOException {
|
||||
this(f, DEFAULT_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
void positionPhysical(long position) throws IOException {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,6 +28,7 @@ package jdk.jfr.internal.consumer;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.DirectoryIteratorException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.ArrayList;
|
||||
@ -45,7 +46,6 @@ import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.Repository;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.management.HiddenWait;;
|
||||
|
||||
public final class RepositoryFiles {
|
||||
@ -57,7 +57,6 @@ public final class RepositoryFiles {
|
||||
}
|
||||
}
|
||||
|
||||
private final FileAccess fileAccess;
|
||||
private final NavigableMap<Long, Path> pathSet = new TreeMap<>();
|
||||
private final Map<Path, Long> pathLookup = new HashMap<>();
|
||||
private final HiddenWait waitObject;
|
||||
@ -65,9 +64,8 @@ public final class RepositoryFiles {
|
||||
private volatile boolean closed;
|
||||
private Path repository;
|
||||
|
||||
public RepositoryFiles(FileAccess fileAccess, Path repository, boolean allowSubDirectory) {
|
||||
public RepositoryFiles(Path repository, boolean allowSubDirectory) {
|
||||
this.repository = repository;
|
||||
this.fileAccess = fileAccess;
|
||||
this.waitObject = repository == null ? WAIT_OBJECT : new HiddenWait();
|
||||
this.allowSubDirectory = allowSubDirectory;
|
||||
}
|
||||
@ -172,14 +170,14 @@ public final class RepositoryFiles {
|
||||
if (repoPath == null) {
|
||||
// Always get the latest repository if 'jcmd JFR.configure
|
||||
// repositorypath=...' has been executed
|
||||
SafePath sf = Repository.getRepository().getRepositoryPath();
|
||||
if (sf == null) {
|
||||
Path path = Repository.getRepository().getRepositoryPath();
|
||||
if (path == null) {
|
||||
return false; // not initialized
|
||||
}
|
||||
repoPath = sf.toPath();
|
||||
repoPath = path;
|
||||
}
|
||||
|
||||
try (DirectoryStream<Path> dirStream = fileAccess.newDirectoryStream(repoPath)) {
|
||||
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(repoPath)) {
|
||||
List<Path> added = new ArrayList<>();
|
||||
Set<Path> current = new HashSet<>();
|
||||
for (Path p : dirStream) {
|
||||
@ -208,7 +206,7 @@ public final class RepositoryFiles {
|
||||
for (Path p : added) {
|
||||
// Only add files that have a complete header
|
||||
// as the JVM may be in progress writing the file
|
||||
long size = fileAccess.fileSize(p);
|
||||
long size = Files.size(p);
|
||||
if (size >= ChunkHeader.headerSize()) {
|
||||
long startNanos = readStartTime(p);
|
||||
if (startNanos != -1) {
|
||||
@ -232,10 +230,10 @@ public final class RepositoryFiles {
|
||||
private Path findSubDirectory(Path repoPath) {
|
||||
FileTime latestTimestamp = null;
|
||||
Path latestPath = null;
|
||||
try (DirectoryStream<Path> dirStream = fileAccess.newDirectoryStream(repoPath)) {
|
||||
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(repoPath)) {
|
||||
for (Path p : dirStream) {
|
||||
String filename = p.getFileName().toString();
|
||||
if (isRepository(filename) && fileAccess.isDirectory(p)) {
|
||||
if (isRepository(filename) && Files.isDirectory(p)) {
|
||||
FileTime timestamp = getLastModified(p);
|
||||
if (timestamp != null) {
|
||||
if (latestPath == null || latestTimestamp.compareTo(timestamp) <= 0) {
|
||||
@ -253,7 +251,7 @@ public final class RepositoryFiles {
|
||||
|
||||
private FileTime getLastModified(Path p) {
|
||||
try {
|
||||
return fileAccess.getLastModified(p);
|
||||
return Files.getLastModifiedTime(p);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
@ -277,7 +275,7 @@ public final class RepositoryFiles {
|
||||
}
|
||||
|
||||
private long readStartTime(Path p) {
|
||||
try (RecordingInput in = new RecordingInput(p.toFile(), fileAccess, 100)) {
|
||||
try (RecordingInput in = new RecordingInput(p.toFile(), 100)) {
|
||||
Logger.log(LogTag.JFR_SYSTEM_PARSER, LogLevel.INFO, "Parsing header for chunk start time");
|
||||
ChunkHeader c = new ChunkHeader(in);
|
||||
return c.getStartNanos();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -38,7 +38,6 @@ import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.internal.LongMap;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.consumer.ChunkHeader;
|
||||
import jdk.jfr.internal.consumer.FileAccess;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
@ -67,7 +66,7 @@ public final class ChunkWriter implements Closeable {
|
||||
public ChunkWriter(Path source, Path destination, Predicate<RecordedEvent> filter) throws IOException {
|
||||
this.destination = destination;
|
||||
this.output = new RecordingOutput(destination.toFile());
|
||||
this.input = new RecordingInput(source.toFile(), FileAccess.UNPRIVILEGED);
|
||||
this.input = new RecordingInput(source.toFile());
|
||||
this.filter = filter;
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -43,8 +43,6 @@ import jdk.jfr.internal.JVMSupport;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.util.ValueFormatter;
|
||||
|
||||
/**
|
||||
@ -134,7 +132,7 @@ abstract class AbstractDCmd {
|
||||
return JVM.getPid();
|
||||
}
|
||||
|
||||
protected final SafePath resolvePath(Recording recording, String filename) throws InvalidPathException {
|
||||
protected Path resolvePath(Recording recording, String filename) throws InvalidPathException {
|
||||
if (filename == null) {
|
||||
return makeGenerated(recording, Paths.get("."));
|
||||
}
|
||||
@ -142,11 +140,11 @@ abstract class AbstractDCmd {
|
||||
if (Files.isDirectory(path)) {
|
||||
return makeGenerated(recording, path);
|
||||
}
|
||||
return new SafePath(path.toAbsolutePath().normalize());
|
||||
return path.toAbsolutePath().normalize();
|
||||
}
|
||||
|
||||
private SafePath makeGenerated(Recording recording, Path directory) {
|
||||
return new SafePath(directory.toAbsolutePath().resolve(JVMSupport.makeFilename(recording)).normalize());
|
||||
private Path makeGenerated(Recording recording, Path directory) {
|
||||
return directory.toAbsolutePath().resolve(JVMSupport.makeFilename(recording)).normalize();
|
||||
}
|
||||
|
||||
protected final Recording findRecording(String name) throws DCmdException {
|
||||
@ -158,7 +156,7 @@ abstract class AbstractDCmd {
|
||||
}
|
||||
}
|
||||
|
||||
protected final void reportOperationComplete(String actionPrefix, String name, SafePath file) {
|
||||
protected final void reportOperationComplete(String actionPrefix, String name, Path file) {
|
||||
print(actionPrefix);
|
||||
print(" recording");
|
||||
if (name != null) {
|
||||
@ -168,7 +166,7 @@ abstract class AbstractDCmd {
|
||||
print(",");
|
||||
try {
|
||||
print(" ");
|
||||
long bytes = SecuritySupport.getFileSize(file);
|
||||
long bytes = Files.size(file);
|
||||
printBytes(bytes);
|
||||
} catch (IOException e) {
|
||||
// Ignore, not essential
|
||||
@ -219,16 +217,12 @@ abstract class AbstractDCmd {
|
||||
print(ValueFormatter.formatTimespan(timespan, separator));
|
||||
}
|
||||
|
||||
protected final void printPath(SafePath path) {
|
||||
protected final void printPath(Path path) {
|
||||
if (path == null) {
|
||||
print("N/A");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
printPath(SecuritySupport.getAbsolutePath(path).toPath());
|
||||
} catch (IOException ioe) {
|
||||
printPath(path.toPath());
|
||||
}
|
||||
println(path.toAbsolutePath().toString());
|
||||
}
|
||||
|
||||
protected final void printHelpText() {
|
||||
@ -237,15 +231,6 @@ abstract class AbstractDCmd {
|
||||
}
|
||||
}
|
||||
|
||||
protected final void printPath(Path path) {
|
||||
try {
|
||||
println(path.toAbsolutePath().toString());
|
||||
} catch (SecurityException e) {
|
||||
// fall back on filename
|
||||
println(path.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private Recording findRecordingById(int id) throws DCmdException {
|
||||
for (Recording r : getFlightRecorder().getRecordings()) {
|
||||
if (r.getId() == id) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,6 +26,7 @@
|
||||
package jdk.jfr.internal.dcmd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import jdk.jfr.FlightRecorder;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
@ -34,7 +35,6 @@ import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.Options;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.Repository;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
|
||||
/**
|
||||
* JFR.configure - invoked from native
|
||||
@ -89,7 +89,7 @@ final class DCmdConfigure extends AbstractDCmd {
|
||||
boolean updated = false;
|
||||
if (repositoryPath != null) {
|
||||
try {
|
||||
SafePath s = new SafePath(repositoryPath);
|
||||
Path s = Path.of(repositoryPath);
|
||||
if (FlightRecorder.isInitialized()) {
|
||||
PrivateAccess.getInstance().getPlatformRecorder().migrate(s);
|
||||
} else {
|
||||
@ -115,7 +115,7 @@ final class DCmdConfigure extends AbstractDCmd {
|
||||
|
||||
if (dumpPath != null) {
|
||||
try {
|
||||
Options.setDumpPath(new SafePath(dumpPath));
|
||||
Options.setDumpPath(Path.of(dumpPath));
|
||||
} catch (IOException e) {
|
||||
throw new DCmdException("Could not set " + dumpPath + " to emergency dump path. " + e.getMessage(), e);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -40,9 +40,8 @@ import jdk.jfr.Recording;
|
||||
import jdk.jfr.internal.PlatformRecorder;
|
||||
import jdk.jfr.internal.PlatformRecording;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.util.ValueParser;
|
||||
import jdk.jfr.internal.WriteableUserPath;
|
||||
import jdk.jfr.internal.WriteablePath;
|
||||
|
||||
/**
|
||||
* JFR.dump
|
||||
@ -126,17 +125,16 @@ final class DCmdDump extends AbstractDCmd {
|
||||
// If a filename exist, use it
|
||||
// if a filename doesn't exist, use destination set earlier
|
||||
// if destination doesn't exist, generate a filename
|
||||
WriteableUserPath wup = null;
|
||||
WriteablePath wp = null;
|
||||
if (recording != null) {
|
||||
PlatformRecording pRecording = PrivateAccess.getInstance().getPlatformRecording(recording);
|
||||
wup = pRecording.getDestination();
|
||||
wp = pRecording.getDestination();
|
||||
}
|
||||
if (filename != null || (filename == null && wup == null) ) {
|
||||
SafePath safe = resolvePath(recording, filename);
|
||||
wup = new WriteableUserPath(safe.toPath());
|
||||
if (filename != null || (filename == null && wp == null) ) {
|
||||
wp = new WriteablePath(resolvePath(recording, filename));
|
||||
}
|
||||
r.dumpStopped(wup);
|
||||
reportOperationComplete("Dumped", name, new SafePath(wup.getRealPathText()));
|
||||
r.dumpStopped(wp);
|
||||
reportOperationComplete("Dumped", name, wp.getReal());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -29,8 +29,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.text.ParseException;
|
||||
import java.time.Duration;
|
||||
import java.util.HashSet;
|
||||
@ -48,8 +46,6 @@ import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.OldObjectSample;
|
||||
import jdk.jfr.internal.PlatformRecording;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.jfc.JFC;
|
||||
import jdk.jfr.internal.jfc.model.JFCModel;
|
||||
@ -154,7 +150,7 @@ final class DCmdStart extends AbstractDCmd {
|
||||
}
|
||||
|
||||
recording.setSettings(s);
|
||||
SafePath safePath = null;
|
||||
Path dumpPath = null;
|
||||
|
||||
// Generate dump filename if user has specified a time-bound recording
|
||||
if (duration != null && path == null) {
|
||||
@ -173,10 +169,10 @@ final class DCmdStart extends AbstractDCmd {
|
||||
// Purposely avoid generating filename in Recording#setDestination due to
|
||||
// security concerns
|
||||
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
|
||||
pr.setDumpDirectory(new SafePath(p));
|
||||
pr.setDumpDirectory(p);
|
||||
} else {
|
||||
safePath = resolvePath(recording, path);
|
||||
recording.setDestination(safePath.toPath());
|
||||
dumpPath = resolvePath(recording, path);
|
||||
recording.setDestination(dumpPath);
|
||||
}
|
||||
} catch (IOException | InvalidPathException e) {
|
||||
recording.close();
|
||||
@ -221,10 +217,10 @@ final class DCmdStart extends AbstractDCmd {
|
||||
recording.setMaxSize(250*1024L*1024L);
|
||||
}
|
||||
|
||||
if (safePath != null && duration != null) {
|
||||
if (dumpPath != null && duration != null) {
|
||||
println(" The result will be written to:");
|
||||
println();
|
||||
printPath(safePath);
|
||||
printPath(dumpPath);
|
||||
} else {
|
||||
println();
|
||||
println();
|
||||
@ -256,7 +252,7 @@ final class DCmdStart extends AbstractDCmd {
|
||||
JFCModel model = new JFCModel(l -> logWarning(l));
|
||||
for (String setting : settings) {
|
||||
try {
|
||||
model.parse(JFC.createSafePath(setting));
|
||||
model.parse(JFC.ofPath(setting));
|
||||
} catch (InvalidPathException | IOException | JFCModelException | ParseException e) {
|
||||
throw new DCmdException(JFC.formatException("Could not", e, setting), e);
|
||||
}
|
||||
@ -463,8 +459,8 @@ final class DCmdStart extends AbstractDCmd {
|
||||
private static String jfcOptions() {
|
||||
try {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (SafePath s : SecuritySupport.getPredefinedJFCFiles()) {
|
||||
String name = JFC.nameFromPath(s.toPath());
|
||||
for (Path s : JFC.getPredefined()) {
|
||||
String name = JFC.nameFromPath(s);
|
||||
JFCModel model = JFCModel.create(s, l -> {});
|
||||
sb.append('\n');
|
||||
sb.append("Options for ").append(name).append(":\n");
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -26,12 +26,12 @@ package jdk.jfr.internal.dcmd;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.WriteableUserPath;
|
||||
import jdk.jfr.internal.WriteablePath;
|
||||
|
||||
/**
|
||||
* JFR.stop
|
||||
@ -47,20 +47,20 @@ final class DCmdStop extends AbstractDCmd {
|
||||
String filename = parser.getOption("filename");
|
||||
try {
|
||||
Recording recording = findRecording(name);
|
||||
WriteableUserPath path = PrivateAccess.getInstance().getPlatformRecording(recording).getDestination();
|
||||
SafePath safePath = path == null ? null : new SafePath(path.getRealPathText());
|
||||
WriteablePath wp = PrivateAccess.getInstance().getPlatformRecording(recording).getDestination();
|
||||
Path path = wp == null ? null : wp.getReal();
|
||||
if (filename != null) {
|
||||
try {
|
||||
// Ensure path is valid. Don't generate safePath if filename == null, as a user may
|
||||
// Ensure path is valid. Don't generate path if filename == null, as a user may
|
||||
// want to stop recording without a dump
|
||||
safePath = resolvePath(null, filename);
|
||||
path = resolvePath(null, filename);
|
||||
recording.setDestination(Paths.get(filename));
|
||||
} catch (IOException | InvalidPathException e) {
|
||||
throw new DCmdException("Failed to stop %s. Could not set destination for \"%s\" to file %s", recording.getName(), filename, e.getMessage());
|
||||
}
|
||||
}
|
||||
recording.stop();
|
||||
reportOperationComplete("Stopped", recording.getName(), safePath);
|
||||
reportOperationComplete("Stopped", recording.getName(), path);
|
||||
recording.close();
|
||||
} catch (InvalidPathException | DCmdException e) {
|
||||
if (filename != null) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,7 +27,6 @@ package jdk.jfr.internal.event;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.jfr.internal.Bits;
|
||||
import jdk.jfr.internal.EventWriterKey;
|
||||
import jdk.jfr.internal.StringPool;
|
||||
import jdk.jfr.internal.JVM;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
@ -37,21 +36,8 @@ import jdk.jfr.internal.consumer.StringParser;
|
||||
// would allow it to write arbitrary data into buffers, potentially from
|
||||
// different threads.
|
||||
//
|
||||
// This is prevented in three ways:
|
||||
//
|
||||
// 1. For code to access the jdk.jfr.internal.event package
|
||||
// at least one event class (for a particular module) must be
|
||||
// registered having FlightRecorderPermission("registerEvent").
|
||||
//
|
||||
// 2. The EventWriter EventWriterFactory::getEventWriter(long) method can only be linked from
|
||||
// the UserEvent::commit() method instrumented by JFR. This is ensured by the JVM.
|
||||
// (The EventWriterFactory class is dynamically generated before the first event
|
||||
// is instrumented. See EventWriterFactoryRecipe)
|
||||
//
|
||||
// 3. Steps 1 and 2 are sufficient to make it fully secure, with or without a Security
|
||||
// Manager, but as an additional measure, the method EventWriterFactory::getEventWriter(long)
|
||||
// requires the caller to provide a key that is hard to guess. The key is generated
|
||||
// into the bytecode of the method invoking getEventWriter(long).
|
||||
// The EventWriter EventWriterFactory::getEventWriter(long) method can only be linked from
|
||||
// the UserEvent::commit() method instrumented by JFR. This is ensured by the JVM.
|
||||
//
|
||||
public final class EventWriter {
|
||||
|
||||
@ -71,6 +57,14 @@ public final class EventWriter {
|
||||
private PlatformEventType eventType;
|
||||
private boolean largeSize = false;
|
||||
|
||||
public static EventWriter getEventWriter() {
|
||||
EventWriter ew = JVM.getEventWriter();
|
||||
if (ew != null) {
|
||||
return ew;
|
||||
}
|
||||
return JVM.newEventWriter();
|
||||
}
|
||||
|
||||
// User code must not be able to instantiate
|
||||
private EventWriter() {
|
||||
threadID = 0;
|
||||
@ -239,11 +233,9 @@ public final class EventWriter {
|
||||
}
|
||||
|
||||
public boolean beginEvent(EventConfiguration configuration, long typeId) {
|
||||
// Malicious code could take the EventConfiguration object from one
|
||||
// event class field and assign it to another. This check makes sure
|
||||
// the event type matches what was added by instrumentation.
|
||||
// This check makes sure the event type matches what was added by instrumentation.
|
||||
if (configuration.getId() != typeId) {
|
||||
EventWriterKey.block();
|
||||
throw new InternalError("Unexpected type id " + typeId);
|
||||
}
|
||||
if (excluded) {
|
||||
// thread is excluded from writing events
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -46,18 +46,33 @@ import jdk.jfr.internal.jfc.model.JFCModelException;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
|
||||
/**
|
||||
* {@link Configuration} factory for JFC files. *
|
||||
*/
|
||||
public final class JFC {
|
||||
private static final Path JFC_DIRECTORY = Utils.getPathInProperty("java.home", "lib/jfr");
|
||||
private static final int BUFFER_SIZE = 8192;
|
||||
private static final int MAXIMUM_FILE_SIZE = 1024 * 1024;
|
||||
private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
|
||||
private static volatile List<KnownConfiguration> knownConfigurations;
|
||||
|
||||
public static List<Path> getPredefined() {
|
||||
List<Path> list = new ArrayList<>();
|
||||
try (var ds = Files.newDirectoryStream(JFC_DIRECTORY)) {
|
||||
for (Path path : ds) {
|
||||
String text = path.toString();
|
||||
if (text.endsWith(".jfc") && !Files.isDirectory(path)) {
|
||||
list.add(path);
|
||||
}
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
Logger.log(LogTag.JFR, LogLevel.WARN, "Could not access .jfc-files in " + JFC_DIRECTORY + ", " + ioe.getMessage());
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a known configuration file (located into a string, but doesn't
|
||||
* parse it until it's being used.
|
||||
@ -66,14 +81,14 @@ public final class JFC {
|
||||
private final String content;
|
||||
private final String filename;
|
||||
private final String name;
|
||||
private final SafePath path;
|
||||
private final Path path;
|
||||
private Configuration configuration;
|
||||
|
||||
public KnownConfiguration(SafePath knownPath) throws IOException {
|
||||
public KnownConfiguration(Path knownPath) throws IOException {
|
||||
this.path = knownPath;
|
||||
this.content = readContent(knownPath);
|
||||
this.name = nameFromPath(knownPath.toPath());
|
||||
this.filename = nullSafeFileName(knownPath.toPath());
|
||||
this.name = nameFromPath(knownPath);
|
||||
this.filename = nullSafeFileName(knownPath);
|
||||
}
|
||||
|
||||
public boolean isNamed(String name) {
|
||||
@ -91,12 +106,12 @@ public final class JFC {
|
||||
return name;
|
||||
}
|
||||
|
||||
private static String readContent(SafePath knownPath) throws IOException {
|
||||
if (SecuritySupport.getFileSize(knownPath) > MAXIMUM_FILE_SIZE) {
|
||||
private static String readContent(Path knownPath) throws IOException {
|
||||
if (Files.size(knownPath) > MAXIMUM_FILE_SIZE) {
|
||||
throw new IOException("Configuration with more than "
|
||||
+ MAXIMUM_FILE_SIZE + " characters can't be read.");
|
||||
}
|
||||
try (InputStream r = SecuritySupport.newFileInputStream(knownPath)) {
|
||||
try (InputStream r = Files.newInputStream(knownPath)) {
|
||||
return JFC.readContent(r);
|
||||
}
|
||||
}
|
||||
@ -114,10 +129,7 @@ public final class JFC {
|
||||
* @throws ParseException if the file can't be parsed
|
||||
* @throws IOException if the file can't be read
|
||||
*
|
||||
* @throws SecurityException if a security manager exists and its
|
||||
* {@code checkRead} method denies read access to the file
|
||||
* @see java.io.File#getPath()
|
||||
* @see java.lang.SecurityManager#checkRead(java.lang.String)
|
||||
*/
|
||||
public static Configuration create(String name, Reader reader) throws IOException, ParseException {
|
||||
try {
|
||||
@ -136,12 +148,12 @@ public final class JFC {
|
||||
*
|
||||
* @param path textual representation of the path
|
||||
*
|
||||
* @return a safe path, not null
|
||||
* @return a path, not null
|
||||
*/
|
||||
public static SafePath createSafePath(String path) {
|
||||
for (SafePath predefined : SecuritySupport.getPredefinedJFCFiles()) {
|
||||
public static Path ofPath(String path) {
|
||||
for (Path predefined : JFC.getPredefined()) {
|
||||
try {
|
||||
String name = JFC.nameFromPath(predefined.toPath());
|
||||
String name = JFC.nameFromPath(predefined);
|
||||
if (name.equals(path) || (name + ".jfc").equals(path)) {
|
||||
return predefined;
|
||||
}
|
||||
@ -149,7 +161,7 @@ public final class JFC {
|
||||
throw new InternalError("Error in predefined .jfc file", e);
|
||||
}
|
||||
}
|
||||
return new SafePath(path);
|
||||
return Path.of(path);
|
||||
}
|
||||
|
||||
|
||||
@ -172,20 +184,19 @@ public final class JFC {
|
||||
|
||||
// Invoked by DCmdStart
|
||||
public static Configuration createKnown(String name) throws IOException, ParseException {
|
||||
// Known name, no need for permission
|
||||
for (KnownConfiguration known : getKnownConfigurations()) {
|
||||
if (known.isNamed(name)) {
|
||||
return known.getConfigurationFile();
|
||||
}
|
||||
}
|
||||
// Check JFC directory
|
||||
SafePath path = SecuritySupport.JFC_DIRECTORY;
|
||||
if (path != null && SecuritySupport.exists(path)) {
|
||||
Path path = JFC_DIRECTORY;
|
||||
if (path != null && Files.exists(path)) {
|
||||
for (String extension : Arrays.asList("", JFCParser.FILE_EXTENSION)) {
|
||||
SafePath file = new SafePath(path.toPath().resolveSibling(name + extension));
|
||||
if (SecuritySupport.exists(file) && !SecuritySupport.isDirectory(file)) {
|
||||
try (Reader r = SecuritySupport.newFileReader(file)) {
|
||||
String jfcName = nameFromPath(file.toPath());
|
||||
Path file = path.resolveSibling(name + extension);
|
||||
if (Files.exists(file) && !Files.isDirectory(file)) {
|
||||
try (Reader r = Files.newBufferedReader(file)) {
|
||||
String jfcName = nameFromPath(file);
|
||||
return JFCParser.createConfiguration(jfcName, r);
|
||||
}
|
||||
}
|
||||
@ -260,7 +271,7 @@ public final class JFC {
|
||||
private static List<KnownConfiguration> getKnownConfigurations() {
|
||||
if (knownConfigurations == null) {
|
||||
List<KnownConfiguration> configProxies = new ArrayList<>();
|
||||
for (SafePath p : SecuritySupport.getPredefinedJFCFiles()) {
|
||||
for (Path p : JFC.getPredefined()) {
|
||||
try {
|
||||
configProxies.add(new KnownConfiguration(p));
|
||||
} catch (IOException ioe) {
|
||||
@ -281,7 +292,7 @@ public final class JFC {
|
||||
throw new NoSuchFileException("Could not locate configuration with name " + name);
|
||||
}
|
||||
|
||||
public static Reader newReader(SafePath sf) throws IOException {
|
||||
public static Reader newReader(Path sf) throws IOException {
|
||||
for (KnownConfiguration c : getKnownConfigurations()) {
|
||||
if (c.path.equals(sf)) {
|
||||
return new StringReader(c.content);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -27,6 +27,7 @@ package jdk.jfr.internal.jfc.model;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Path;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@ -35,7 +36,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.jfc.JFC;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
@ -65,7 +65,7 @@ public final class JFCModel {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public void parse(SafePath file) throws IOException, JFCModelException, ParseException {
|
||||
public void parse(Path file) throws IOException, JFCModelException, ParseException {
|
||||
JFCModel model = JFCModel.create(file, logger);
|
||||
for (var entry : model.controls.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
@ -80,7 +80,7 @@ public final class JFCModel {
|
||||
}
|
||||
}
|
||||
|
||||
public static JFCModel create(SafePath file, Consumer<String> logger) throws IOException, JFCModelException, ParseException{
|
||||
public static JFCModel create(Path file, Consumer<String> logger) throws IOException, JFCModelException, ParseException{
|
||||
if (file.toString().equals("none")) {
|
||||
XmlConfiguration configuration = new XmlConfiguration();
|
||||
configuration.setAttribute("version", "2.0");
|
||||
@ -154,7 +154,7 @@ public final class JFCModel {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void saveToFile(SafePath path) throws IOException {
|
||||
public void saveToFile(Path path) throws IOException {
|
||||
try (PrintWriter p = new PrintWriter(path.toFile(), UTF_8)) {
|
||||
PrettyPrinter pp = new PrettyPrinter(p);
|
||||
pp.print(configuration);
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,13 +25,12 @@
|
||||
package jdk.jfr.internal.management;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.time.LocalDateTime;
|
||||
import java.io.IOException;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.util.ValueFormatter;
|
||||
import jdk.jfr.internal.consumer.FileAccess;
|
||||
|
||||
// Allows a remote streaming client to create chunk files
|
||||
// with same naming scheme as the JVM.
|
||||
@ -40,23 +39,12 @@ public final class ChunkFilename {
|
||||
private static final String FILE_EXTENSION = ".jfr";
|
||||
|
||||
private final Path directory;
|
||||
private final FileAccess fileAcess;
|
||||
|
||||
private Path lastPath;
|
||||
private int counter;
|
||||
|
||||
public static ChunkFilename newUnpriviliged(Path directory) {
|
||||
return new ChunkFilename(directory, FileAccess.UNPRIVILEGED);
|
||||
}
|
||||
|
||||
public static ChunkFilename newPriviliged(Path directory) {
|
||||
return new ChunkFilename(directory, SecuritySupport.PRIVILEGED);
|
||||
}
|
||||
|
||||
private ChunkFilename(Path directory, FileAccess fileAccess) {
|
||||
// Avoid malicious implementations of Path interface
|
||||
this.directory = Paths.get(directory.toString());
|
||||
this.fileAcess = fileAccess;
|
||||
public ChunkFilename(Path directory) {
|
||||
this.directory = directory;
|
||||
}
|
||||
|
||||
public String next(LocalDateTime time) throws IOException {
|
||||
@ -65,7 +53,7 @@ public final class ChunkFilename {
|
||||
|
||||
// If less than one file per second (typically case)
|
||||
if (lastPath == null || !p.equals(lastPath)) {
|
||||
if (!fileAcess.exists(p)) {
|
||||
if (!Files.exists(p)) {
|
||||
counter = 1; // reset counter
|
||||
lastPath = p;
|
||||
return p.toString();
|
||||
@ -77,7 +65,7 @@ public final class ChunkFilename {
|
||||
String extendedName = makeExtendedName(filename, counter);
|
||||
p = directory.resolve(extendedName);
|
||||
counter++;
|
||||
if (!fileAcess.exists(p)) {
|
||||
if (!Files.exists(p)) {
|
||||
return p.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -34,8 +34,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.security.AccessControlContext;
|
||||
|
||||
import jdk.jfr.Configuration;
|
||||
import jdk.jfr.EventSettings;
|
||||
import jdk.jfr.EventType;
|
||||
@ -49,15 +47,12 @@ import jdk.jfr.internal.Logger;
|
||||
import jdk.jfr.internal.MetadataRepository;
|
||||
import jdk.jfr.internal.PlatformRecording;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
import jdk.jfr.internal.SecuritySupport;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.util.Utils;
|
||||
import jdk.jfr.internal.util.ValueFormatter;
|
||||
import jdk.jfr.internal.util.ValueParser;
|
||||
import jdk.jfr.internal.WriteableUserPath;
|
||||
import jdk.jfr.internal.WriteablePath;
|
||||
import jdk.jfr.internal.consumer.AbstractEventStream;
|
||||
import jdk.jfr.internal.consumer.EventDirectoryStream;
|
||||
import jdk.jfr.internal.consumer.FileAccess;
|
||||
|
||||
/**
|
||||
* The management API in module jdk.management.jfr should be built on top of the
|
||||
@ -84,7 +79,6 @@ public final class ManagementSupport {
|
||||
//
|
||||
public static List<EventType> getEventTypes() {
|
||||
// would normally be checked when a Flight Recorder instance is created
|
||||
SecuritySupport.checkAccessFlightRecorder();
|
||||
if (JVMSupport.isNotAvailable()) {
|
||||
return List.of();
|
||||
}
|
||||
@ -121,17 +115,16 @@ public final class ManagementSupport {
|
||||
// requires access to jdk.jfr.internal.PlatformRecording
|
||||
public static String getDestinationOriginalText(Recording recording) {
|
||||
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
|
||||
WriteableUserPath wup = pr.getDestination();
|
||||
return wup == null ? null : wup.getOriginalText();
|
||||
WriteablePath wp = pr.getDestination();
|
||||
return wp == null ? null : wp.getPath().toString();
|
||||
}
|
||||
|
||||
// Needed to check if destination can be set, so FlightRecorderMXBean::setRecordingOption
|
||||
// can abort if not all data is valid
|
||||
public static void checkSetDestination(Recording recording, String destination) throws IOException{
|
||||
public static void checkSetDestination(Recording recording, String destination) throws IOException {
|
||||
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
|
||||
if(destination != null){
|
||||
WriteableUserPath wup = new WriteableUserPath(Paths.get(destination));
|
||||
pr.checkSetDestination(wup);
|
||||
pr.checkSetDestination(new WriteablePath(Paths.get(destination)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -143,7 +136,7 @@ public final class ManagementSupport {
|
||||
// Needed callback to detect when a chunk has been parsed.
|
||||
public static void removePath(Recording recording, Path path) {
|
||||
PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording);
|
||||
pr.removePath(new SafePath(path));
|
||||
pr.removePath(path);
|
||||
}
|
||||
|
||||
// Needed callback to detect when a chunk has been parsed.
|
||||
@ -169,14 +162,10 @@ public final class ManagementSupport {
|
||||
// EventStream::onMetadataData need to supply MetadataEvent
|
||||
// with configuration objects
|
||||
public static EventStream newEventDirectoryStream(
|
||||
@SuppressWarnings("removal")
|
||||
AccessControlContext acc,
|
||||
Path directory,
|
||||
List<Configuration> confs) throws IOException {
|
||||
return new EventDirectoryStream(
|
||||
acc,
|
||||
directory,
|
||||
FileAccess.UNPRIVILEGED,
|
||||
null,
|
||||
confs,
|
||||
false
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,9 +30,9 @@ import jdk.jfr.internal.MetadataRepository;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.PrivateAccess;
|
||||
/**
|
||||
* Base class for periodic Java events.
|
||||
* Class for periodic Java events.
|
||||
*/
|
||||
abstract class JavaEventTask extends EventTask {
|
||||
final class JavaEventTask extends EventTask {
|
||||
private final Runnable runnable;
|
||||
|
||||
public JavaEventTask(Class<? extends Event> eventClass, Runnable runnable) {
|
||||
@ -48,7 +48,8 @@ abstract class JavaEventTask extends EventTask {
|
||||
return PrivateAccess.getInstance().getPlatformEventType(eventType);
|
||||
}
|
||||
|
||||
protected final Runnable getRunnable() {
|
||||
return runnable;
|
||||
@Override
|
||||
public void execute(long timestamp, PeriodicType periodicType) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,11 +25,8 @@
|
||||
package jdk.jfr.internal.periodic;
|
||||
|
||||
/**
|
||||
* Lookup key that can safely be used in a {@code Map}.
|
||||
* <p>
|
||||
* {@code Runnable} objects can't be used with {@code LinkedHashMap} as it
|
||||
* invokes {@code hashCode} and {@code equals}, for example when resizing the
|
||||
* {@code Map}, possibly in a non-secure context.
|
||||
* Lookup key that can be used in a {@code Map} in
|
||||
* case hashCode and equals are incorrectly overridden.
|
||||
* <p>
|
||||
* {@code IdentityHashMap} can't be used as it will not preserve order.
|
||||
*/
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -24,7 +24,6 @@
|
||||
*/
|
||||
package jdk.jfr.internal.periodic;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
@ -58,18 +57,14 @@ public final class PeriodicEvents {
|
||||
// State only to be read and modified by periodic task thread
|
||||
private static long lastTimeMillis;
|
||||
|
||||
public static void addJDKEvent(Class<? extends Event> eventClass, Runnable runnable) {
|
||||
taskRepository.add(new JDKEventTask(eventClass, runnable));
|
||||
public static void addJavaEvent(Class<? extends Event> eventClass, Runnable runnable) {
|
||||
taskRepository.add(new JavaEventTask(eventClass, runnable));
|
||||
}
|
||||
|
||||
public static void addJVMEvent(PlatformEventType eventType) {
|
||||
taskRepository.add(new JVMEventTask(eventType));
|
||||
}
|
||||
|
||||
public static void addUserEvent(@SuppressWarnings("removal") AccessControlContext acc, Class<? extends Event> eventClass, Runnable runnable) {
|
||||
taskRepository.add(new UserEventTask(acc, eventClass, runnable));
|
||||
}
|
||||
|
||||
public static boolean removeEvent(Runnable runnable) {
|
||||
return taskRepository.removeTask(runnable);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -40,9 +40,6 @@ import jdk.jfr.internal.Logger;
|
||||
* / \
|
||||
* / \
|
||||
* JVMEventTask JavaEventTask
|
||||
* / \
|
||||
* / \
|
||||
* UserEventTask JDKEventTask
|
||||
* </pre>
|
||||
* <p>
|
||||
* State modifications should only be done from the periodic task thread.
|
||||
@ -127,8 +124,8 @@ abstract class PeriodicTask {
|
||||
try {
|
||||
execute(timestamp, periodicType);
|
||||
} catch (Throwable e) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Exception occurred during execution of " + name);
|
||||
String msg = "Exception occurred during execution of " + name + ". " + e.getMessage();
|
||||
Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jfr.internal.periodic;
|
||||
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.util.Objects;
|
||||
|
||||
import jdk.internal.event.Event;
|
||||
import jdk.jfr.internal.LogLevel;
|
||||
import jdk.jfr.internal.LogTag;
|
||||
import jdk.jfr.internal.Logger;
|
||||
|
||||
/**
|
||||
* Class to be used with user-defined events that runs untrusted code.
|
||||
* <p>
|
||||
* This class can be removed once the Security Manager is no longer supported.
|
||||
*/
|
||||
final class UserEventTask extends JavaEventTask {
|
||||
@SuppressWarnings("removal")
|
||||
private final AccessControlContext controlContext;
|
||||
|
||||
public UserEventTask(@SuppressWarnings("removal") AccessControlContext controlContext, Class<? extends Event> eventClass, Runnable runnable) {
|
||||
super(eventClass, runnable);
|
||||
this.controlContext = Objects.requireNonNull(controlContext);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
@Override
|
||||
public void execute(long timestamp, PeriodicType periodicType) {
|
||||
AccessController.doPrivileged((PrivilegedAction<Void>) () -> {
|
||||
execute();
|
||||
return null;
|
||||
}, controlContext);
|
||||
}
|
||||
|
||||
private void execute() {
|
||||
try {
|
||||
getRunnable().run();
|
||||
if (Logger.shouldLog(LogTag.JFR_EVENT, LogLevel.DEBUG)) {
|
||||
Logger.log(LogTag.JFR_EVENT, LogLevel.DEBUG, "Executed periodic task for " + getEventType().getLogName());
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// Prevent malicious user to propagate exception callback in the wrong context
|
||||
Logger.log(LogTag.JFR_EVENT, LogLevel.WARN, "Exception occurred during execution of period task for " + getEventType().getLogName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,9 +28,10 @@ package jdk.jfr.internal.settings;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
|
||||
abstract class BooleanSetting extends JDKSettingControl {
|
||||
abstract class BooleanSetting extends SettingControl {
|
||||
private final PlatformEventType eventType;
|
||||
private final String defaultValue;
|
||||
private String value;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -31,6 +31,7 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import jdk.jfr.Description;
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
@ -44,7 +45,7 @@ import jdk.jfr.internal.util.ValueParser;
|
||||
@Description("Limit running time of event")
|
||||
@Name(Type.SETTINGS_PREFIX + "Cutoff")
|
||||
@Timespan
|
||||
public final class CutoffSetting extends JDKSettingControl {
|
||||
public final class CutoffSetting extends SettingControl {
|
||||
public static final String DEFAULT_VALUE = ValueParser.INFINITY;
|
||||
private String value = DEFAULT_VALUE;
|
||||
private final PlatformEventType eventType;
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.jfr.internal.settings;
|
||||
|
||||
import jdk.jfr.SettingControl;
|
||||
|
||||
/**
|
||||
* SettingControls that derive from this class avoids executing settings
|
||||
* modifications in a AccessController.doPrivilege(...) block.
|
||||
*/
|
||||
public abstract class JDKSettingControl extends SettingControl {
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -33,13 +33,14 @@ import java.util.Set;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.Type;
|
||||
|
||||
@MetadataDefinition
|
||||
@Label("Level")
|
||||
@Name(Type.SETTINGS_PREFIX + "Level")
|
||||
public final class LevelSetting extends JDKSettingControl {
|
||||
public final class LevelSetting extends SettingControl {
|
||||
private final PlatformEventType eventType;
|
||||
private final List<String> levels;
|
||||
private String value;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -32,6 +32,7 @@ import jdk.jfr.Description;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.util.ValueParser;
|
||||
@ -41,7 +42,7 @@ import static jdk.jfr.internal.util.ValueParser.MISSING;
|
||||
@Label("Period")
|
||||
@Description("Record event at interval")
|
||||
@Name(Type.SETTINGS_PREFIX + "Period")
|
||||
public final class PeriodSetting extends JDKSettingControl {
|
||||
public final class PeriodSetting extends SettingControl {
|
||||
private static final long typeId = Type.getTypeId(PeriodSetting.class);
|
||||
|
||||
public static final String EVERY_CHUNK = "everyChunk";
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -34,6 +34,7 @@ import jdk.jfr.Description;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.Timespan;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.Type;
|
||||
@ -44,7 +45,7 @@ import jdk.jfr.internal.util.ValueParser;
|
||||
@Name(Type.SETTINGS_PREFIX + "Threshold")
|
||||
@Description("Record event with duration above or equal to threshold")
|
||||
@Timespan
|
||||
public final class ThresholdSetting extends JDKSettingControl {
|
||||
public final class ThresholdSetting extends SettingControl {
|
||||
public static final String DEFAULT_VALUE = "0 ns";
|
||||
private static final long typeId = Type.getTypeId(ThresholdSetting.class);
|
||||
private String value = DEFAULT_VALUE;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, Datadog, Inc. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
@ -36,6 +36,7 @@ import jdk.jfr.Description;
|
||||
import jdk.jfr.Label;
|
||||
import jdk.jfr.MetadataDefinition;
|
||||
import jdk.jfr.Name;
|
||||
import jdk.jfr.SettingControl;
|
||||
import jdk.jfr.internal.PlatformEventType;
|
||||
import jdk.jfr.internal.Throttle;
|
||||
import jdk.jfr.internal.Type;
|
||||
@ -47,7 +48,7 @@ import jdk.jfr.internal.util.Utils;
|
||||
@Label("Throttle")
|
||||
@Description("Throttles the emission rate for an event")
|
||||
@Name(Type.SETTINGS_PREFIX + "Throttle")
|
||||
public final class ThrottleSetting extends JDKSettingControl {
|
||||
public final class ThrottleSetting extends SettingControl {
|
||||
public static final String DEFAULT_VALUE = Throttle.DEFAULT;
|
||||
private final PlatformEventType eventType;
|
||||
private String value = DEFAULT_VALUE;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -52,7 +52,7 @@ abstract class Command {
|
||||
List<Command> commands = new ArrayList<>();
|
||||
commands.add(new Print());
|
||||
// Uncomment when developing new queries for the view command
|
||||
// commands.add(new Query());
|
||||
commands.add(new Query());
|
||||
commands.add(new View());
|
||||
commands.add(new Configure());
|
||||
commands.add(new Metadata());
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -38,7 +38,6 @@ import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.jfc.JFC;
|
||||
import jdk.jfr.internal.jfc.model.AbortException;
|
||||
import jdk.jfr.internal.jfc.model.JFCModel;
|
||||
@ -129,7 +128,7 @@ final class Configure extends Command {
|
||||
stream.println("Options for " + name + ":");
|
||||
stream.println();
|
||||
try {
|
||||
SafePath path = JFC.createSafePath(name);
|
||||
Path path = JFC.ofPath(name);
|
||||
JFCModel parameters = JFCModel.create(path, l -> stream.println("Warning! " + l));
|
||||
for (XmlInput input : parameters.getInputs()) {
|
||||
stream.println(" " + input.getOptionSyntax());
|
||||
@ -144,7 +143,7 @@ final class Configure extends Command {
|
||||
public void execute(Deque<String> options) throws UserSyntaxException, UserDataException {
|
||||
boolean interactive = false;
|
||||
boolean log = false;
|
||||
SafePath output = null;
|
||||
Path output = null;
|
||||
Map<String, String> keyValues = new LinkedHashMap<>();
|
||||
int optionCount = options.size();
|
||||
while (optionCount > 0) {
|
||||
@ -192,7 +191,7 @@ final class Configure extends Command {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void configure(boolean interactive, boolean log, SafePath output, Map<String, String> options) throws UserDataException {
|
||||
private void configure(boolean interactive, boolean log, Path output, Map<String, String> options) throws UserDataException {
|
||||
UserInterface ui = new UserInterface();
|
||||
if (log) {
|
||||
SettingsLog.enable();
|
||||
@ -201,14 +200,14 @@ final class Configure extends Command {
|
||||
model.setLabel("Custom");
|
||||
for (String input : inputFiles) {
|
||||
try {
|
||||
model.parse(JFC.createSafePath(input));
|
||||
model.parse(JFC.ofPath(input));
|
||||
} catch (InvalidPathException | IOException | JFCModelException | ParseException e) {
|
||||
throw new UserDataException(JFC.formatException("could not", e, input));
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (output == null) {
|
||||
output = new SafePath(Path.of("custom.jfc"));
|
||||
output = Path.of("custom.jfc");
|
||||
}
|
||||
for (var option : options.entrySet()) {
|
||||
model.configure(option.getKey(), option.getValue());
|
||||
@ -230,7 +229,7 @@ final class Configure extends Command {
|
||||
}
|
||||
model.saveToFile(output);
|
||||
ui.println("Configuration written successfully to:");
|
||||
ui.println(output.toPath().toAbsolutePath().toString());
|
||||
ui.println(output.toAbsolutePath().toString());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new UserDataException(iae.getMessage());
|
||||
} catch (FileNotFoundException ffe) {
|
||||
@ -246,7 +245,7 @@ final class Configure extends Command {
|
||||
}
|
||||
}
|
||||
|
||||
private static SafePath filename(UserInterface ui, SafePath file) throws AbortException {
|
||||
private static Path filename(UserInterface ui, Path file) throws AbortException {
|
||||
ui.println();
|
||||
ui.println("Filename: " + file + " (default)");
|
||||
while (true) {
|
||||
@ -256,7 +255,7 @@ final class Configure extends Command {
|
||||
return file;
|
||||
}
|
||||
if (line.endsWith(".jfc")) {
|
||||
return new SafePath(line);
|
||||
return Path.of(line);
|
||||
}
|
||||
ui.println("Filename must end with .jfc.");
|
||||
} catch (InvalidPathException ipe) {
|
||||
@ -265,14 +264,14 @@ final class Configure extends Command {
|
||||
}
|
||||
}
|
||||
|
||||
private SafePath makeJFCPath(String file) throws UserDataException, UserSyntaxException {
|
||||
private Path makeJFCPath(String file) throws UserDataException, UserSyntaxException {
|
||||
if (file.startsWith("--")) {
|
||||
throw new UserSyntaxException("missing file");
|
||||
}
|
||||
try {
|
||||
Path path = Path.of(file).toAbsolutePath();
|
||||
ensureFileExtension(path, ".jfc");
|
||||
return new SafePath(path);
|
||||
return path;
|
||||
} catch (IOError ioe) {
|
||||
throw new UserDataException("i/o error reading file '" + file + "', " + ioe.getMessage());
|
||||
} catch (InvalidPathException ipe) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -41,7 +41,6 @@ import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.internal.consumer.ChunkHeader;
|
||||
import jdk.jfr.internal.consumer.FileAccess;
|
||||
import jdk.jfr.internal.consumer.RecordingInput;
|
||||
import jdk.jfr.internal.util.UserDataException;
|
||||
import jdk.jfr.internal.util.UserSyntaxException;
|
||||
@ -166,7 +165,7 @@ final class Disassemble extends Command {
|
||||
}
|
||||
|
||||
private List<Long> findChunkSizes(Path p) throws IOException {
|
||||
try (RecordingInput input = new RecordingInput(p.toFile(), FileAccess.UNPRIVILEGED)) {
|
||||
try (RecordingInput input = new RecordingInput(p.toFile())) {
|
||||
List<Long> sizes = new ArrayList<>();
|
||||
ChunkHeader ch = new ChunkHeader(input);
|
||||
sizes.add(ch.getSize());
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -41,7 +41,6 @@ import jdk.jfr.EventType;
|
||||
import jdk.jfr.internal.MetadataDescriptor;
|
||||
import jdk.jfr.internal.Type;
|
||||
import jdk.jfr.internal.consumer.ChunkHeader;
|
||||
import jdk.jfr.internal.consumer.FileAccess;
|
||||
import jdk.jfr.internal.consumer.RecordingInput;
|
||||
import jdk.jfr.internal.util.UserDataException;
|
||||
import jdk.jfr.internal.util.UserSyntaxException;
|
||||
@ -93,7 +92,7 @@ final class Summary extends Command {
|
||||
long totalDuration = 0;
|
||||
long chunks = 0;
|
||||
|
||||
try (RecordingInput input = new RecordingInput(p.toFile(), FileAccess.UNPRIVILEGED)) {
|
||||
try (RecordingInput input = new RecordingInput(p.toFile())) {
|
||||
ChunkHeader first = new ChunkHeader(input);
|
||||
ChunkHeader ch = first;
|
||||
String eventPrefix = Type.EVENT_NAME_PREFIX;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -22,23 +22,33 @@
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jfr.internal;
|
||||
package jdk.jfr.internal.util;
|
||||
|
||||
import jdk.jfr.internal.event.EventWriter;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.SimpleFileVisitor;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
// This class is not directly used but renamed to
|
||||
// jdk.jfr.internal.event.EventWriterFactory and loaded dynamically
|
||||
// when the first event class is bytecode instrumented.
|
||||
// See JVMUpcalls and EventWriterKey::ensureEventWriterFactory()
|
||||
public final class EventWriterFactoryRecipe {
|
||||
private static final long KEY = EventWriterKey.getKey();
|
||||
public final class DirectoryCleaner extends SimpleFileVisitor<Path> {
|
||||
|
||||
public static EventWriter getEventWriter(long key) {
|
||||
if (key == KEY) {
|
||||
EventWriter ew = JVM.getEventWriter();
|
||||
return ew != null ? ew : JVM.newEventWriter();
|
||||
public static void clear(Path path) throws IOException {
|
||||
Files.walkFileTree(path, new DirectoryCleaner());
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException {
|
||||
Files.delete(path);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
|
||||
if (exc != null) {
|
||||
throw exc;
|
||||
}
|
||||
EventWriterKey.block();
|
||||
return null; // Can't reach here.
|
||||
Files.delete(dir);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,7 +25,9 @@
|
||||
|
||||
package jdk.jfr.internal.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Repeatable;
|
||||
@ -448,4 +450,13 @@ public final class Utils {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Path getPathInProperty(String prop, String subPath) {
|
||||
String path = System.getProperty(prop);
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
File file = subPath == null ? new File(path) : new File(path, subPath);
|
||||
return file.toPath().toAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -151,7 +151,7 @@ final class DiskRepository implements Closeable {
|
||||
public DiskRepository(Path path, boolean deleteDirectory) throws IOException {
|
||||
this.directory = path;
|
||||
this.deleteDirectory = deleteDirectory;
|
||||
this.chunkFilename = ChunkFilename.newUnpriviliged(path);
|
||||
this.chunkFilename = new ChunkFilename(path);
|
||||
}
|
||||
|
||||
public synchronized void write(byte[] bytes) throws IOException {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,9 +30,6 @@ import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.text.ParseException;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
@ -67,7 +64,6 @@ import jdk.jfr.Configuration;
|
||||
import jdk.jfr.EventType;
|
||||
import jdk.jfr.FlightRecorder;
|
||||
import jdk.jfr.FlightRecorderListener;
|
||||
import jdk.jfr.FlightRecorderPermission;
|
||||
import jdk.jfr.Recording;
|
||||
import jdk.jfr.RecordingState;
|
||||
import jdk.jfr.internal.management.ManagementSupport;
|
||||
@ -80,12 +76,9 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
|
||||
private final NotificationListener listener;
|
||||
private final NotificationFilter filter;
|
||||
private final Object handback;
|
||||
@SuppressWarnings("removal")
|
||||
private final AccessControlContext context;
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public MXBeanListener(NotificationListener listener, NotificationFilter filter, Object handback) {
|
||||
this.context = AccessController.getContext();
|
||||
this.listener = listener;
|
||||
this.filter = filter;
|
||||
this.handback = handback;
|
||||
@ -93,13 +86,7 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
public void recordingStateChanged(Recording recording) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
sendNotification(createNotification(recording));
|
||||
return null;
|
||||
}
|
||||
}, context);
|
||||
sendNotification(createNotification(recording));
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,25 +111,21 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
|
||||
|
||||
@Override
|
||||
public void startRecording(long id) {
|
||||
MBeanUtils.checkControl();
|
||||
getExistingRecording(id).start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stopRecording(long id) {
|
||||
MBeanUtils.checkControl();
|
||||
return getExistingRecording(id).stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeRecording(long id) {
|
||||
MBeanUtils.checkControl();
|
||||
getExistingRecording(id).close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long openStream(long id, Map<String, String> options) throws IOException {
|
||||
MBeanUtils.checkControl();
|
||||
if (!FlightRecorder.isInitialized()) {
|
||||
throw new IllegalArgumentException("No recording available with id " + id);
|
||||
}
|
||||
@ -169,19 +152,16 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
|
||||
|
||||
@Override
|
||||
public void closeStream(long streamIdentifier) throws IOException {
|
||||
MBeanUtils.checkControl();
|
||||
streamHandler.getStream(streamIdentifier).close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readStream(long streamIdentifier) throws IOException {
|
||||
MBeanUtils.checkMonitor();
|
||||
return streamHandler.getStream(streamIdentifier).read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RecordingInfo> getRecordings() {
|
||||
MBeanUtils.checkMonitor();
|
||||
if (!FlightRecorder.isInitialized()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@ -190,60 +170,39 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
|
||||
|
||||
@Override
|
||||
public List<ConfigurationInfo> getConfigurations() {
|
||||
MBeanUtils.checkMonitor();
|
||||
return MBeanUtils.transformList(Configuration.getConfigurations(), ConfigurationInfo::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<EventTypeInfo> getEventTypes() {
|
||||
MBeanUtils.checkMonitor();
|
||||
@SuppressWarnings("removal")
|
||||
List<EventType> eventTypes = AccessController.doPrivileged(new PrivilegedAction<List<EventType>>() {
|
||||
@Override
|
||||
public List<EventType> run() {
|
||||
return ManagementSupport.getEventTypes();
|
||||
}
|
||||
}, null, new FlightRecorderPermission("accessFlightRecorder"));
|
||||
|
||||
return MBeanUtils.transformList(eventTypes, EventTypeInfo::new);
|
||||
return MBeanUtils.transformList(ManagementSupport.getEventTypes(), EventTypeInfo::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getRecordingSettings(long recording) throws IllegalArgumentException {
|
||||
MBeanUtils.checkMonitor();
|
||||
return getExistingRecording(recording).getSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordingSettings(long recording, Map<String, String> settings) throws IllegalArgumentException {
|
||||
Objects.requireNonNull(settings, "settings");
|
||||
MBeanUtils.checkControl();
|
||||
getExistingRecording(recording).setSettings(settings);
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
@Override
|
||||
public long newRecording() {
|
||||
MBeanUtils.checkControl();
|
||||
getRecorder(); // ensure notification listener is setup
|
||||
return AccessController.doPrivileged(new PrivilegedAction<Recording>() {
|
||||
@Override
|
||||
public Recording run() {
|
||||
return new Recording();
|
||||
}
|
||||
}, null, new FlightRecorderPermission("accessFlightRecorder")).getId();
|
||||
return new Recording().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long takeSnapshot() {
|
||||
MBeanUtils.checkControl();
|
||||
return getRecorder().takeSnapshot().getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setConfiguration(long recording, String contents) throws IllegalArgumentException {
|
||||
Objects.requireNonNull(contents, "contents");
|
||||
MBeanUtils.checkControl();
|
||||
try {
|
||||
Configuration c = Configuration.create(new StringReader(contents));
|
||||
getExistingRecording(recording).setSettings(c.getSettings());
|
||||
@ -255,7 +214,6 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
|
||||
@Override
|
||||
public void setPredefinedConfiguration(long recording, String configurationName) throws IllegalArgumentException {
|
||||
Objects.requireNonNull(configurationName, "configurationName");
|
||||
MBeanUtils.checkControl();
|
||||
Recording r = getExistingRecording(recording);
|
||||
for (Configuration c : Configuration.getConfigurations()) {
|
||||
if (c.getName().equals(configurationName)) {
|
||||
@ -269,14 +227,12 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
|
||||
@Override
|
||||
public void copyTo(long recording, String outputFile) throws IOException {
|
||||
Objects.requireNonNull(outputFile, "outputFile");
|
||||
MBeanUtils.checkControl();
|
||||
getExistingRecording(recording).dump(Paths.get(outputFile));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecordingOptions(long recording, Map<String, String> options) throws IllegalArgumentException {
|
||||
Objects.requireNonNull(options, "options");
|
||||
MBeanUtils.checkControl();
|
||||
// Make local copy to prevent concurrent modification
|
||||
Map<String, String> ops = new HashMap<String, String>(options);
|
||||
for (Map.Entry<String, String> entry : ops.entrySet()) {
|
||||
@ -315,7 +271,6 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
|
||||
|
||||
@Override
|
||||
public Map<String, String> getRecordingOptions(long recording) throws IllegalArgumentException {
|
||||
MBeanUtils.checkMonitor();
|
||||
Recording r = getExistingRecording(recording);
|
||||
Map<String, String> options = HashMap.newHashMap(10);
|
||||
options.put(OPTION_DUMP_ON_EXIT, String.valueOf(r.getDumpOnExit()));
|
||||
@ -330,8 +285,7 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
|
||||
}
|
||||
|
||||
@Override
|
||||
public long cloneRecording(long id, boolean stop) throws IllegalStateException, SecurityException {
|
||||
MBeanUtils.checkControl();
|
||||
public long cloneRecording(long id, boolean stop) throws IllegalStateException {
|
||||
return getRecording(id).copy(stop).getId();
|
||||
}
|
||||
|
||||
@ -397,16 +351,11 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
|
||||
}
|
||||
|
||||
@SuppressWarnings("removal")
|
||||
private FlightRecorder getRecorder() throws SecurityException {
|
||||
private FlightRecorder getRecorder() {
|
||||
// Synchronize on some private object that is always available
|
||||
synchronized (streamHandler) {
|
||||
if (recorder == null) {
|
||||
recorder = AccessController.doPrivileged(new PrivilegedAction<FlightRecorder>() {
|
||||
@Override
|
||||
public FlightRecorder run() {
|
||||
return FlightRecorder.getFlightRecorder();
|
||||
}
|
||||
}, null, new FlightRecorderPermission("accessFlightRecorder"));
|
||||
recorder = FlightRecorder.getFlightRecorder();
|
||||
}
|
||||
return recorder;
|
||||
}
|
||||
@ -425,13 +374,7 @@ final class FlightRecorderMXBeanImpl extends StandardEmitterMBean implements Fli
|
||||
public void addNotificationListener(NotificationListener listener, NotificationFilter filter, Object handback) {
|
||||
MXBeanListener mxbeanListener = new MXBeanListener(listener, filter, handback);
|
||||
listeners.add(mxbeanListener);
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run(){
|
||||
FlightRecorder.addListener(mxbeanListener);
|
||||
return null;
|
||||
}
|
||||
}, null, new FlightRecorderPermission("accessFlightRecorder"));
|
||||
FlightRecorder.addListener(mxbeanListener);
|
||||
super.addNotificationListener(listener, filter, handback);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,8 +25,6 @@
|
||||
package jdk.management.jfr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.management.ManagementPermission;
|
||||
import java.security.Permission;
|
||||
import java.time.DateTimeException;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
@ -43,9 +41,6 @@ import jdk.jfr.internal.management.ManagementSupport;
|
||||
|
||||
final class MBeanUtils {
|
||||
|
||||
private static final Permission monitor = new ManagementPermission("monitor");
|
||||
private static final Permission control = new ManagementPermission("control");
|
||||
|
||||
static ObjectName createObjectName() {
|
||||
try {
|
||||
return new ObjectName(FlightRecorderMXBean.MXBEAN_NAME);
|
||||
@ -54,22 +49,6 @@ final class MBeanUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static void checkControl() {
|
||||
@SuppressWarnings("removal")
|
||||
SecurityManager secManager = System.getSecurityManager();
|
||||
if (secManager != null) {
|
||||
secManager.checkPermission(control);
|
||||
}
|
||||
}
|
||||
|
||||
static void checkMonitor() {
|
||||
@SuppressWarnings("removal")
|
||||
SecurityManager secManager = System.getSecurityManager();
|
||||
if (secManager != null) {
|
||||
secManager.checkPermission(monitor);
|
||||
}
|
||||
}
|
||||
|
||||
static <T, R> List<R> transformList(List<T> source, Function<T, R> function) {
|
||||
return source.stream().map(function).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -32,8 +32,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.security.AccessControlContext;
|
||||
import java.security.AccessController;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
@ -44,7 +42,6 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.function.Consumer;
|
||||
import java.security.AccessControlException;
|
||||
import javax.management.JMX;
|
||||
import javax.management.MBeanServerConnection;
|
||||
import javax.management.ObjectName;
|
||||
@ -149,8 +146,6 @@ public final class RemoteRecordingStream implements EventStream {
|
||||
final FlightRecorderMXBean mbean;
|
||||
final long recordingId;
|
||||
final EventStream stream;
|
||||
@SuppressWarnings("removal")
|
||||
final AccessControlContext accessControllerContext;
|
||||
final DiskRepository repository;
|
||||
final Instant creationTime;
|
||||
final Object lock = new Object();
|
||||
@ -205,9 +200,7 @@ public final class RemoteRecordingStream implements EventStream {
|
||||
private RemoteRecordingStream(MBeanServerConnection connection, Path directory, boolean delete) throws IOException {
|
||||
Objects.requireNonNull(connection, "connection");
|
||||
Objects.requireNonNull(directory, "directory");
|
||||
accessControllerContext = AccessController.getContext();
|
||||
// Make sure users can't implement malicious version of a Path object.
|
||||
path = Paths.get(directory.toString());
|
||||
path = directory;
|
||||
if (!Files.exists(path)) {
|
||||
throw new IOException("Download directory doesn't exist");
|
||||
}
|
||||
@ -219,7 +212,7 @@ public final class RemoteRecordingStream implements EventStream {
|
||||
creationTime = Instant.now();
|
||||
mbean = createProxy(connection);
|
||||
recordingId = createRecording();
|
||||
stream = ManagementSupport.newEventDirectoryStream(accessControllerContext, path, configurations(mbean));
|
||||
stream = ManagementSupport.newEventDirectoryStream(path, configurations(mbean));
|
||||
stream.setStartTime(Instant.MIN);
|
||||
repository = new DiskRepository(path, delete);
|
||||
ManagementSupport.setOnChunkCompleteHandler(stream, new ChunkConsumer(repository));
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,12 +25,12 @@ package jdk.jfr.jcmd;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import jdk.jfr.internal.Repository;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.jfr.internal.Options;
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.Utils;
|
||||
@ -131,17 +131,17 @@ public class TestJcmdConfigure {
|
||||
|
||||
try {
|
||||
JcmdHelper.jcmd("JFR.configure", REPOSITORYPATH_SETTING_1);
|
||||
SafePath initialPath = Repository.getRepository().getRepositoryPath();
|
||||
Path initialPath = Repository.getRepository().getRepositoryPath();
|
||||
|
||||
JcmdHelper.jcmd("JFR.configure", REPOSITORYPATH_SETTING_1);
|
||||
SafePath samePath = Repository.getRepository().getRepositoryPath();
|
||||
Path samePath = Repository.getRepository().getRepositoryPath();
|
||||
Asserts.assertTrue(samePath.equals(initialPath));
|
||||
|
||||
List<String> lines = Files.readAllLines(Paths.get(JFR_UNIFIED_LOG_FILE));
|
||||
Asserts.assertTrue(lines.stream().anyMatch(l->l.contains(findWhat)));
|
||||
|
||||
JcmdHelper.jcmd("JFR.configure", REPOSITORYPATH_SETTING_2);
|
||||
SafePath changedPath = Repository.getRepository().getRepositoryPath();
|
||||
Path changedPath = Repository.getRepository().getRepositoryPath();
|
||||
|
||||
Asserts.assertFalse(changedPath.equals(initialPath));
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,7 +28,7 @@ import jdk.jfr.Registered;
|
||||
@Registered(false)
|
||||
public class MyCommitRegisteredFalseEvent extends E implements Runnable {
|
||||
public void myCommit() {
|
||||
PlaceholderEventWriterFactory.getEventWriter(4711L);
|
||||
PlaceholderEventWriter.getEventWriter();
|
||||
throw new RuntimeException("Should not reach here");
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,7 +28,7 @@ import jdk.jfr.Registered;
|
||||
@Registered(true)
|
||||
public class MyCommitRegisteredTrueEvent extends E implements Runnable {
|
||||
public void myCommit() {
|
||||
PlaceholderEventWriterFactory.getEventWriter(4711L);
|
||||
PlaceholderEventWriter.getEventWriter();
|
||||
throw new RuntimeException("Should not reach here");
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,7 +25,7 @@ package jdk.jfr.jvm;
|
||||
// Class used by TestGetEventWriter
|
||||
public class NonEvent implements Runnable {
|
||||
public void commit() {
|
||||
PlaceholderEventWriter ew = PlaceholderEventWriterFactory.getEventWriter(4711L);
|
||||
PlaceholderEventWriter ew = PlaceholderEventWriter.getEventWriter();;
|
||||
throw new RuntimeException("Should not reach here " + ew);
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,4 +30,8 @@ package jdk.jfr.jvm;
|
||||
// will be replaced with "jdk.jfr.internal.event.EventWriter"
|
||||
public class PlaceholderEventWriter {
|
||||
|
||||
public static PlaceholderEventWriter getEventWriter() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
package jdk.jfr.jvm;
|
||||
|
||||
// Purpose of this class is to have something to
|
||||
// statically link against for TestGetEventWriter.
|
||||
//
|
||||
// When the class is loaded "jdk.jfr.jvm.PlaceholderEventWriterFactory"
|
||||
// will be replaced with "jdk.jfr.internal.event.EventWriterFactory"
|
||||
public class PlaceholderEventWriterFactory {
|
||||
|
||||
public static PlaceholderEventWriter getEventWriter(long value) {
|
||||
throw new RuntimeException("Test error, PlaceholderEventWriterFactory class should have been replaced with EventWriterFactory");
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,7 +28,7 @@ import jdk.jfr.Registered;
|
||||
@Registered(false)
|
||||
public class RegisteredFalseEvent extends E {
|
||||
public void commit() {
|
||||
PlaceholderEventWriterFactory.getEventWriter(4711L);
|
||||
PlaceholderEventWriter.getEventWriter();
|
||||
throw new RuntimeException("Should not reach here");
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -28,7 +28,7 @@ import jdk.jfr.Registered;
|
||||
@Registered(true)
|
||||
public class RegisteredTrueEvent extends E {
|
||||
public void commit() {
|
||||
PlaceholderEventWriterFactory.getEventWriter(4711L);
|
||||
PlaceholderEventWriter.getEventWriter();
|
||||
throw new RuntimeException("Should not reach here");
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -30,7 +30,7 @@ public class StaticCommitEvent implements Runnable {
|
||||
int value;
|
||||
|
||||
public static void commit(long start, long duration, String message, int value) {
|
||||
PlaceholderEventWriterFactory.getEventWriter(4711L);
|
||||
PlaceholderEventWriter.getEventWriter();
|
||||
throw new RuntimeException("Should not reach here");
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2022, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -46,7 +46,6 @@ import jdk.vm.ci.runtime.JVMCI;
|
||||
* jdk.internal.vm.ci/jdk.vm.ci.runtime
|
||||
*
|
||||
* @compile PlaceholderEventWriter.java
|
||||
* @compile PlaceholderEventWriterFactory.java
|
||||
* @compile E.java
|
||||
* @compile NonEvent.java
|
||||
* @compile RegisteredTrueEvent.java
|
||||
@ -80,7 +79,6 @@ import jdk.vm.ci.runtime.JVMCI;
|
||||
* jdk.internal.vm.ci/jdk.vm.ci.runtime
|
||||
*
|
||||
* @compile PlaceholderEventWriter.java
|
||||
* @compile PlaceholderEventWriterFactory.java
|
||||
* @compile E.java
|
||||
* @compile NonEvent.java
|
||||
* @compile RegisteredTrueEvent.java
|
||||
@ -105,10 +103,10 @@ public class TestGetEventWriter {
|
||||
InitializationEvent e = new InitializationEvent();
|
||||
e.commit();
|
||||
}
|
||||
// Make sure EventWriterFactory can be accessed.
|
||||
Class<?> clazz = Class.forName("jdk.jfr.internal.event.EventWriterFactory");
|
||||
// Make sure EventWriter class can be accessed.
|
||||
Class<?> clazz = Class.forName("jdk.jfr.internal.event.EventWriter");
|
||||
if (clazz == null) {
|
||||
throw new Exception("Test error, not able to access jdk.jfr.internal.event.EventWriterFactory class");
|
||||
throw new Exception("Test error, not able to access jdk.jfr.internal.event.EventWriter class");
|
||||
}
|
||||
testRegisteredTrueEvent();
|
||||
testRegisteredFalseEvent();
|
||||
@ -122,7 +120,7 @@ public class TestGetEventWriter {
|
||||
|
||||
// The class does not inherit jdk.jfr.Event and, as such, does not implement the
|
||||
// API. It has its own stand-alone "commit()V", which is not an override, that
|
||||
// attempts to resolve and link against EventWriterFactory. This user implementation
|
||||
// attempts to resolve and link against EventWriter. This user implementation
|
||||
// is not blessed for linkage.
|
||||
private static void testNonEvent() throws Throwable {
|
||||
Runnable e = newEventObject("NonEvent");
|
||||
@ -178,7 +176,7 @@ public class TestGetEventWriter {
|
||||
}
|
||||
|
||||
// The user has implemented another method, "myCommit()V", not an override nor
|
||||
// overload. that attempts to resolve and link EventWriterFactory. This will fail,
|
||||
// overload. that attempts to resolve and link EventWriter. This will fail,
|
||||
// because "myCommit()V" is not blessed for linkage.
|
||||
private static void testMyCommitRegisteredTrue() throws Throwable {
|
||||
Runnable e = newEventObject("MyCommitRegisteredTrueEvent");
|
||||
@ -230,10 +228,9 @@ public class TestGetEventWriter {
|
||||
public void myCommit() throws Throwable {
|
||||
try {
|
||||
Class<?> ew = Class.forName("jdk.jfr.internal.event.EventWriter");
|
||||
MethodType t = MethodType.methodType(ew, List.of(long.class));
|
||||
Class<?> factory = Class.forName("jdk.jfr.internal.event.EventWriterFactory");
|
||||
MethodHandle mh = MethodHandles.lookup().findStatic(factory, "getEventWriter", t);
|
||||
mh.invoke(Long.valueOf(4711)); // throws IllegalAccessException
|
||||
MethodType t = MethodType.methodType(ew, List.of());
|
||||
MethodHandle mh = MethodHandles.lookup().findStatic(ew, "getEventWriter", t);
|
||||
mh.invoke(); // throws IllegalAccessException
|
||||
} catch (ClassNotFoundException | SecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -262,8 +259,8 @@ public class TestGetEventWriter {
|
||||
public void myCommit() throws Throwable {
|
||||
Class<?> c;
|
||||
try {
|
||||
c = Class.forName("jdk.jfr.internal.event.EventWriterFactory");
|
||||
Method m = c.getMethod("getEventWriter", new Class[] {long.class});
|
||||
c = Class.forName("jdk.jfr.internal.event.EventWriter");
|
||||
Method m = c.getMethod("getEventWriter", new Class[0]);
|
||||
m.invoke(null, Long.valueOf(4711)); // throws InternalError
|
||||
} catch (ClassNotFoundException | SecurityException e) {
|
||||
throw new RuntimeException(e);
|
||||
@ -283,7 +280,7 @@ public class TestGetEventWriter {
|
||||
} catch (InternalError ie) {
|
||||
if (ie.getCause() instanceof IllegalAccessException iaex) {
|
||||
if (iaex.getCause() instanceof IllegalAccessError iae) {
|
||||
if (iae.getMessage().contains("getEventWriter(long)")) {
|
||||
if (iae.getMessage().contains("getEventWriter()")) {
|
||||
// OK, as expected
|
||||
return;
|
||||
}
|
||||
@ -345,7 +342,6 @@ public class TestGetEventWriter {
|
||||
byte[] bytes = is.readAllBytes();
|
||||
is.close();
|
||||
bytes = replace(bytes, "jdk/jfr/jvm/E", "jdk/jfr/Event");
|
||||
bytes = replace(bytes, "jdk/jfr/jvm/PlaceholderEventWriterFactory", "jdk/jfr/internal/event/EventWriterFactory");
|
||||
bytes = replace(bytes, "jdk/jfr/jvm/PlaceholderEventWriter", "jdk/jfr/internal/event/EventWriter");
|
||||
BytesClassLoader bc = new BytesClassLoader(bytes, fullName);
|
||||
Class<?> clazz = bc.loadClass(fullName);
|
||||
@ -372,7 +368,7 @@ public class TestGetEventWriter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that JVMCI prevents unblessed access to {@code EventWriterFactory.getEventWriter(long)}.
|
||||
* Checks that JVMCI prevents unblessed access to {@code EventWriter.getEventWriter()}.
|
||||
*/
|
||||
private static void checkJVMCI(Class<?> eventClass, String commitName) throws Throwable {
|
||||
MetaAccessProvider metaAccess = JVMCI.getRuntime().getHostJVMCIBackend().getMetaAccess();
|
||||
@ -380,7 +376,7 @@ public class TestGetEventWriter {
|
||||
ConstantPool cp = commit.getConstantPool();
|
||||
|
||||
// Search for first INVOKESTATIC instruction in commit method which is expected
|
||||
// to be the call to jdk.jfr.internal.event.EventWriterFactory.getEventWriter(long).
|
||||
// to be the call to jdk.jfr.internal.event.EventWriter.getEventWriter().
|
||||
final int INVOKESTATIC = 184;
|
||||
byte[] code = commit.getCode();
|
||||
for (int bci = 0; bci < code.length; bci++) {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -35,7 +35,6 @@ import jdk.jfr.Recording;
|
||||
import jdk.jfr.consumer.RecordedEvent;
|
||||
import jdk.jfr.consumer.RecordingFile;
|
||||
import jdk.jfr.internal.Repository;
|
||||
import jdk.jfr.internal.SecuritySupport.SafePath;
|
||||
import jdk.test.lib.Asserts;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
@ -80,7 +79,7 @@ public class TestAssemble {
|
||||
expectedCount += countEventInRecording(tmp);
|
||||
}
|
||||
|
||||
SafePath repository = Repository.getRepository().getRepositoryPath();
|
||||
Path repository = Repository.getRepository().getRepositoryPath();
|
||||
Path destinationPath = Paths.get("reconstructed.jfr");
|
||||
|
||||
String directory = repository.toString();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user