src/jdk.jfr/share/classes/jdk/jfr/internal/dcmd/DCmdStart.java
changeset 50113 caf115bb98ad
child 50226 408021edf22f
equal deleted inserted replaced
50112:7a2a740815b7 50113:caf115bb98ad
       
     1 /*
       
     2  * Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package jdk.jfr.internal.dcmd;
       
    26 
       
    27 import java.io.IOException;
       
    28 import java.nio.file.InvalidPathException;
       
    29 import java.nio.file.Paths;
       
    30 import java.text.ParseException;
       
    31 import java.time.Duration;
       
    32 import java.util.HashMap;
       
    33 import java.util.Map;
       
    34 
       
    35 import jdk.jfr.FlightRecorder;
       
    36 import jdk.jfr.Recording;
       
    37 import jdk.jfr.internal.JVM;
       
    38 import jdk.jfr.internal.SecuritySupport.SafePath;
       
    39 import jdk.jfr.internal.Type;
       
    40 import jdk.jfr.internal.Utils;
       
    41 import jdk.jfr.internal.jfc.JFC;
       
    42 
       
    43 /**
       
    44  * JFR.start
       
    45  *
       
    46  */
       
    47 //Instantiated by native
       
    48 final class DCmdStart extends AbstractDCmd {
       
    49 
       
    50     /**
       
    51      * Execute JFR.start.
       
    52      *
       
    53      * @param name optional name that can be used to identify recording.
       
    54      * @param configurations names of settings files to use, i.e. "default" or
       
    55      *        "default.jfc".
       
    56      * @param delay delay before recording is started, in nanoseconds. Must be
       
    57      *        at least 1 second.
       
    58      * @param duration duration of the recording, in nanoseconds. Must be at
       
    59      *        least 1 second.
       
    60      * @param disk if recording should be persisted to disk
       
    61      * @param path file path where recording data should be written
       
    62      * @param maxAge how long recording data should be kept in the disk
       
    63      *        repository, or <code>0</code> if no limit should be set.
       
    64      *
       
    65      * @param maxSize the minimum amount data to keep in the disk repository
       
    66      *        before it is discarded, or <code>0</code> if no limit should be
       
    67      *        set.
       
    68      *
       
    69      * @param dumpOnExit if recording should dump on exit
       
    70      *
       
    71      * @return result output
       
    72      *
       
    73      * @throws DCmdException if recording could not be started
       
    74      */
       
    75     @SuppressWarnings("resource")
       
    76     public String execute(String name, String[] configurations, Long delay, Long duration, Boolean disk, String path, Long maxAge, Long maxSize, Boolean dumpOnExit, Boolean pathToGcRoots) throws DCmdException {
       
    77         if (name != null) {
       
    78             try {
       
    79                 Integer.parseInt(name);
       
    80                 throw new DCmdException("Name of recording can't be numeric");
       
    81             } catch (NumberFormatException nfe) {
       
    82                 // ok, can't be mixed up with name
       
    83             }
       
    84         }
       
    85 
       
    86         if (duration == null && Boolean.FALSE.equals(dumpOnExit) && path != null) {
       
    87             throw new DCmdException("Filename can only be set for a time bound recording or if dumponexit=true. Set duration/dumponexit or omit filename.");
       
    88         }
       
    89         if (dumpOnExit == null && path != null) {
       
    90             dumpOnExit = Boolean.TRUE;
       
    91         }
       
    92 
       
    93         Map<String, String> s = new HashMap<>();
       
    94 
       
    95         if (configurations == null || configurations.length == 0) {
       
    96             configurations = new String[] { "default" };
       
    97         }
       
    98 
       
    99         for (String configName : configurations) {
       
   100             try {
       
   101                 s.putAll(JFC.createKnown(configName).getSettings());
       
   102             } catch (IOException | ParseException e) {
       
   103                 throw new DCmdException("Could not parse setting " + configurations[0], e);
       
   104             }
       
   105         }
       
   106 
       
   107         Utils.updateSettingPathToGcRoots(s, pathToGcRoots);
       
   108 
       
   109         if (duration != null) {
       
   110             if (duration < 1000L * 1000L * 1000L) {
       
   111                 // to avoid typo, duration below 1s makes no sense
       
   112                 throw new DCmdException("Could not start recording, duration must be at least 1 second.");
       
   113             }
       
   114         }
       
   115 
       
   116         if (delay != null) {
       
   117             if (delay < 1000L * 1000L * 1000) {
       
   118                 // to avoid typo, delay shorter than 1s makes no sense.
       
   119                 throw new DCmdException("Could not start recording, delay must be at least 1 second.");
       
   120             }
       
   121         }
       
   122 
       
   123         if (!FlightRecorder.isInitialized() && delay == null) {
       
   124             initializeWithForcedInstrumentation(s);
       
   125         }
       
   126 
       
   127         Recording recording = new Recording();
       
   128         if (name == null) {
       
   129             recording.setName("Recording-" + recording.getId());
       
   130         } else {
       
   131             recording.setName(name);
       
   132         }
       
   133 
       
   134         if (disk != null) {
       
   135             recording.setToDisk(disk.booleanValue());
       
   136         }
       
   137         recording.setSettings(s);
       
   138 
       
   139         if (path != null) {
       
   140             try {
       
   141                 recording.setDestination(Paths.get(path));
       
   142             } catch (IOException | InvalidPathException e) {
       
   143                 recording.close();
       
   144                 throw new DCmdException("Could not start recording, not able to write to file %s. %s ", path, e.getMessage());
       
   145             }
       
   146         }
       
   147 
       
   148         if (maxAge != null) {
       
   149             recording.setMaxAge(Duration.ofNanos(maxAge));
       
   150         }
       
   151 
       
   152         if (maxSize != null) {
       
   153             recording.setMaxSize(maxSize);
       
   154         }
       
   155 
       
   156         if (duration != null) {
       
   157             recording.setDuration(Duration.ofNanos(duration));
       
   158         }
       
   159 
       
   160         if (dumpOnExit != null) {
       
   161             recording.setDumpOnExit(dumpOnExit);
       
   162         }
       
   163 
       
   164         if (delay != null) {
       
   165             Duration dDelay = Duration.ofNanos(delay);
       
   166             recording.scheduleStart(dDelay);
       
   167             print("Recording " + recording.getId() + " scheduled to start in ");
       
   168             printTimespan(dDelay, " ");
       
   169             print(".");
       
   170         } else {
       
   171             recording.start();
       
   172             print("Started recording " + recording.getId() + ".");
       
   173         }
       
   174 
       
   175         if (recording.isToDisk() && duration == null && maxAge == null && maxSize == null) {
       
   176             print(" No limit specified, using maxsize=250MB as default.");
       
   177             recording.setMaxSize(250*1024L*1024L);
       
   178         }
       
   179 
       
   180         if (path != null && duration != null) {
       
   181             println(" The result will be written to:");
       
   182             println();
       
   183             printPath(new SafePath(path));
       
   184         } else {
       
   185             println();
       
   186             println();
       
   187             String cmd = duration == null ? "dump" : "stop";
       
   188             String fileOption = path == null ? "filename=FILEPATH " : "";
       
   189             String recordingspecifier = "name=" + recording.getId();
       
   190             // if user supplied a name, use it.
       
   191             if (name != null) {
       
   192                 recordingspecifier = "name=" + quoteIfNeeded(name);
       
   193             }
       
   194             print("Use JFR." + cmd + " " + recordingspecifier + " " + fileOption + "to copy recording data to file.");
       
   195             println();
       
   196         }
       
   197         return getResult();
       
   198     }
       
   199 
       
   200     // Instruments JDK-events on class load to reduce startup time
       
   201     private void initializeWithForcedInstrumentation(Map<String, String> settings) {
       
   202         if (!hasJDKEvents(settings)) {
       
   203             return;
       
   204         }
       
   205         JVM jvm = JVM.getJVM();
       
   206         try {
       
   207             jvm.setForceInstrumentation(true);
       
   208             FlightRecorder.getFlightRecorder();
       
   209         } finally {
       
   210             jvm.setForceInstrumentation(false);
       
   211         }
       
   212     }
       
   213 
       
   214     private boolean hasJDKEvents(Map<String, String> settings) {
       
   215         String[] eventNames = new String[7];
       
   216         eventNames[0] = "FileRead";
       
   217         eventNames[1] = "FileWrite";
       
   218         eventNames[2] = "SocketRead";
       
   219         eventNames[3] = "SocketWrite";
       
   220         eventNames[4] = "JavaErrorThrow";
       
   221         eventNames[5] = "JavaExceptionThrow";
       
   222         eventNames[6] = "FileForce";
       
   223         for (String eventName : eventNames) {
       
   224             if ("true".equals(settings.get(Type.EVENT_NAME_PREFIX + eventName + "#enabled"))) {
       
   225                 return true;
       
   226             }
       
   227         }
       
   228         return false;
       
   229     }
       
   230 }