11/*
2- * Copyright (c) 2016, 2023 , Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2016, 2024 , Oracle and/or its affiliates. All rights reserved.
33 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44 *
55 * This code is free software; you can redistribute it and/or modify it
3838import java .nio .file .StandardOpenOption ;
3939import java .security .AccessControlContext ;
4040import java .security .AccessController ;
41+ import java .security .PrivilegedActionException ;
42+ import java .security .PrivilegedExceptionAction ;
4143import java .time .Duration ;
4244import java .time .Instant ;
4345import java .time .LocalDateTime ;
@@ -76,7 +78,7 @@ public final class PlatformRecording implements AutoCloseable {
7678 private boolean toDisk = true ;
7779 private String name ;
7880 private boolean dumpOnExit ;
79- private SafePath dumpOnExitDirectory = new SafePath ( "." ) ;
81+ private SafePath dumpDirectory ;
8082 // Timestamp information
8183 private Instant stopTime ;
8284 private Instant startTime ;
@@ -89,7 +91,7 @@ public final class PlatformRecording implements AutoCloseable {
8991 private TimerTask stopTask ;
9092 private TimerTask startTask ;
9193 @ SuppressWarnings ("removal" )
92- private AccessControlContext noDestinationDumpOnExitAccessControlContext ;
94+ private final AccessControlContext dumpDirectoryControlContext ;
9395 private boolean shouldWriteActiveRecordingEvent = true ;
9496 private Duration flushInterval = Duration .ofSeconds (1 );
9597 private long finalStartChunkNanos = Long .MIN_VALUE ;
@@ -99,11 +101,11 @@ public final class PlatformRecording implements AutoCloseable {
99101 PlatformRecording (PlatformRecorder recorder , long id ) {
100102 // Typically the access control context is taken
101103 // when you call dump(Path) or setDestination(Path),
102- // but if no destination is set and dumpOnExit=true
104+ // but if no destination is set and the filename is auto-generated,
103105 // the control context of the recording is taken when the
104106 // Recording object is constructed. This works well for
105107 // -XX:StartFlightRecording and JFR.dump
106- this .noDestinationDumpOnExitAccessControlContext = AccessController .getContext ();
108+ this .dumpDirectoryControlContext = AccessController .getContext ();
107109 this .id = id ;
108110 this .recorder = recorder ;
109111 this .name = String .valueOf (id );
@@ -174,7 +176,9 @@ public boolean stop(String reason) {
174176 newState = getState ();
175177 }
176178 WriteableUserPath dest = getDestination ();
177-
179+ if (dest == null && dumpDirectory != null ) {
180+ dest = makeDumpPath ();
181+ }
178182 if (dest != null ) {
179183 try {
180184 dumpStopped (dest );
@@ -191,6 +195,33 @@ public boolean stop(String reason) {
191195 return true ;
192196 }
193197
198+ @ SuppressWarnings ("removal" )
199+ public WriteableUserPath makeDumpPath () {
200+ try {
201+ String name = JVMSupport .makeFilename (getRecording ());
202+ return AccessController .doPrivileged (new PrivilegedExceptionAction <WriteableUserPath >() {
203+ @ Override
204+ public WriteableUserPath run () throws Exception {
205+ SafePath p = dumpDirectory ;
206+ if (p == null ) {
207+ p = new SafePath ("." );
208+ }
209+ return new WriteableUserPath (p .toPath ().resolve (name ));
210+ }
211+ }, dumpDirectoryControlContext );
212+ } catch (PrivilegedActionException e ) {
213+ Throwable t = e .getCause ();
214+ if (t instanceof SecurityException ) {
215+ Logger .log (LogTag .JFR , LogLevel .WARN , "Not allowed to create dump path for recording " + recording .getId () + " on exit." );
216+ }
217+ if (t instanceof IOException ) {
218+ Logger .log (LogTag .JFR , LogLevel .WARN , "Could not dump " + recording .getId () + " on exit." );
219+ }
220+ return null ;
221+ }
222+ }
223+
224+
194225 public void scheduleStart (Duration delay ) {
195226 synchronized (recorder ) {
196227 ensureOkForSchedule ();
@@ -697,11 +728,6 @@ void clearDestination() {
697728 destination = null ;
698729 }
699730
700- @ SuppressWarnings ("removal" )
701- public AccessControlContext getNoDestinationDumpOnExitAccessControlContext () {
702- return noDestinationDumpOnExitAccessControlContext ;
703- }
704-
705731 void setShouldWriteActiveRecordingEvent (boolean shouldWrite ) {
706732 this .shouldWriteActiveRecordingEvent = shouldWrite ;
707733 }
@@ -828,12 +854,13 @@ private static List<RepositoryChunk> reduceFromEnd(Long maxSize, List<Repository
828854 return result ;
829855 }
830856
831- public void setDumpOnExitDirectory (SafePath directory ) {
832- this .dumpOnExitDirectory = directory ;
833- }
834-
835- public SafePath getDumpOnExitDirectory () {
836- return this .dumpOnExitDirectory ;
857+ /**
858+ * Sets the dump directory.
859+ * <p>
860+ * Only to be used by DCmdStart.
861+ */
862+ public void setDumpDirectory (SafePath directory ) {
863+ this .dumpDirectory = directory ;
837864 }
838865
839866 public void setFlushInterval (Duration interval ) {
0 commit comments