< prev index next >

src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/PathUtilities.java

Print this page




   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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.debug;
  24 

  25 import java.io.IOException;


  26 import java.nio.file.InvalidPathException;
  27 import java.nio.file.Path;
  28 import java.nio.file.Paths;
  29 import java.util.HashMap;
  30 import java.util.concurrent.atomic.AtomicInteger;
  31 import java.util.concurrent.atomic.AtomicLong;
  32 
  33 import org.graalvm.compiler.options.OptionKey;
  34 import org.graalvm.compiler.options.OptionValues;
  35 
  36 /**
  37  * Miscellaneous methods for modifying and generating file system paths.
  38  */
  39 public class PathUtilities {
  40 
  41     private static final AtomicLong globalTimeStamp = new AtomicLong();
  42     /**
  43      * This generates a per thread persistent id to aid mapping related dump files with each other.
  44      */
  45     private static final ThreadLocal<PerThreadSequence> threadDumpId = new ThreadLocal<>();
  46     private static final AtomicInteger dumpId = new AtomicInteger();
  47 
  48     static class PerThreadSequence {
  49         final int threadID;
  50         HashMap<String, Integer> sequences = new HashMap<>(2);
  51 
  52         PerThreadSequence(int threadID) {
  53             this.threadID = threadID;
  54         }
  55 
  56         String generateID(String extension) {
  57             Integer box = sequences.get(extension);
  58             if (box == null) {
  59                 sequences.put(extension, 1);
  60                 return Integer.toString(threadID);
  61             } else {
  62                 sequences.put(extension, box + 1);
  63                 return Integer.toString(threadID) + '-' + box;
  64             }
  65         }
  66     }
  67 
  68     private static String getThreadDumpId(String extension) {
  69         PerThreadSequence id = threadDumpId.get();
  70         if (id == null) {
  71             id = new PerThreadSequence(dumpId.incrementAndGet());
  72             threadDumpId.set(id);
  73         }
  74         return id.generateID(extension);
  75     }
  76 
  77     /**
  78      * Prepends a period (i.e., {@code '.'}) to an non-null, non-empty string representation a file
  79      * extension if the string does not already start with a period.
  80      *
  81      * @return {@code ext} unmodified if it is null, empty or already starts with a period other
  82      *         {@code "." + ext}
  83      */
  84     public static String formatExtension(String ext) {
  85         if (ext == null || ext.length() == 0) {
  86             return "";
  87         }
  88         return "." + ext;
  89     }
  90 
  91     /**
  92      * Gets a time stamp for the current process. This method will always return the same value for
  93      * the current VM execution.
  94      */
  95     public static long getGlobalTimeStamp() {
  96         if (globalTimeStamp.get() == 0) {
  97             globalTimeStamp.compareAndSet(0, System.currentTimeMillis());
  98         }
  99         return globalTimeStamp.get();
 100     }
 101 
 102     /**
 103      * Generates a {@link Path} using the format "%s-%d_%d%s" with the {@code baseNameOption}, a
 104      * {@link #getGlobalTimeStamp() global timestamp} , {@link #getThreadDumpId a per thread unique
 105      * id} and an optional {@code extension}.
 106      *
 107      * @return the output file path or null if the flag is null
 108      */
 109     public static Path getPath(OptionValues options, OptionKey<String> baseNameOption, String extension) throws IOException {
 110         return getPath(options, baseNameOption, extension, true);
 111     }
 112 
 113     /**
 114      * Generate a {@link Path} using the format "%s-%d_%s" with the {@code baseNameOption}, a
 115      * {@link #getGlobalTimeStamp() global timestamp} and an optional {@code extension} .
 116      *
 117      * @return the output file path or null if the flag is null
 118      */
 119     public static Path getPathGlobal(OptionValues options, OptionKey<String> baseNameOption, String extension) throws IOException {
 120         return getPath(options, baseNameOption, extension, false);
 121     }
 122 
 123     private static Path getPath(OptionValues options, OptionKey<String> baseNameOption, String extension, boolean includeThreadId) throws IOException {
 124         if (baseNameOption.getValue(options) == null) {
 125             return null;
 126         }
 127         String ext = formatExtension(extension);
 128         final String name = includeThreadId
 129                         ? String.format("%s-%d_%s%s", baseNameOption.getValue(options), getGlobalTimeStamp(), getThreadDumpId(ext), ext)
 130                         : String.format("%s-%d%s", baseNameOption.getValue(options), getGlobalTimeStamp(), ext);
 131         Path result = Paths.get(name);
 132         if (result.isAbsolute()) {
 133             return result;
 134         }
 135         Path dumpDir = DebugOptions.getDumpDirectory(options);
 136         return dumpDir.resolve(name).normalize();
 137     }
 138 
 139     /**
 140      * Gets a value based on {@code name} that can be passed to {@link Paths#get(String, String...)}
 141      * without causing an {@link InvalidPathException}.
 142      *
 143      * @return {@code name} with all characters invalid for the current file system replaced by
 144      *         {@code '_'}
 145      */
 146     public static String sanitizeFileName(String name) {
 147         try {
 148             Paths.get(name);

 149             return name;

 150         } catch (InvalidPathException e) {
 151             // fall through
 152         }
 153         StringBuilder buf = new StringBuilder(name.length());
 154         for (int i = 0; i < name.length(); i++) {
 155             char c = name.charAt(i);

 156             try {
 157                 Paths.get(String.valueOf(c));


 158             } catch (InvalidPathException e) {
 159                 buf.append('_');
 160             }
 161             buf.append(c);

 162         }
 163         return buf.toString();
 164     }






















































 165 }


   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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.debug;
  24 
  25 import java.io.File;
  26 import java.io.IOException;
  27 import java.nio.file.FileAlreadyExistsException;
  28 import java.nio.file.Files;
  29 import java.nio.file.InvalidPathException;
  30 import java.nio.file.Path;
  31 import java.nio.file.Paths;


  32 import java.util.concurrent.atomic.AtomicLong;
  33 
  34 import org.graalvm.compiler.options.OptionKey;
  35 import org.graalvm.compiler.options.OptionValues;
  36 
  37 /**
  38  * Miscellaneous methods for modifying and generating file system paths.
  39  */
  40 public class PathUtilities {
  41 
  42     private static final AtomicLong globalTimeStamp = new AtomicLong();
















































  43 
  44     /**
  45      * Gets a time stamp for the current process. This method will always return the same value for
  46      * the current VM execution.
  47      */
  48     public static long getGlobalTimeStamp() {
  49         if (globalTimeStamp.get() == 0) {
  50             globalTimeStamp.compareAndSet(0, System.currentTimeMillis());
  51         }
  52         return globalTimeStamp.get();
  53     }
  54 
  55     /**





































  56      * Gets a value based on {@code name} that can be passed to {@link Paths#get(String, String...)}
  57      * without causing an {@link InvalidPathException}.
  58      *
  59      * @return {@code name} with all characters invalid for the current file system replaced by
  60      *         {@code '_'}
  61      */
  62     public static String sanitizeFileName(String name) {
  63         try {
  64             Path path = Paths.get(name);
  65             if (path.getNameCount() == 0) {
  66                 return name;
  67             }
  68         } catch (InvalidPathException e) {
  69             // fall through
  70         }
  71         StringBuilder buf = new StringBuilder(name.length());
  72         for (int i = 0; i < name.length(); i++) {
  73             char c = name.charAt(i);
  74             if (c != File.separatorChar && c != ' ' && !Character.isISOControl(c)) {
  75                 try {
  76                     Paths.get(String.valueOf(c));
  77                     buf.append(c);
  78                     continue;
  79                 } catch (InvalidPathException e) {

  80                 }
  81             }
  82             buf.append('_');
  83         }
  84         return buf.toString();
  85     }
  86 
  87     /**
  88      * A maximum file name length supported by most file systems. There is no platform independent
  89      * way to get this in Java.
  90      */
  91     private static final int MAX_FILE_NAME_LENGTH = 255;
  92 
  93     private static final String ELLIPSIS = "...";
  94 
  95     static Path createUnique(OptionValues options, OptionKey<String> baseNameOption, String id, String label, String ext, boolean createDirectory) throws IOException {
  96         String uniqueTag = "";
  97         int dumpCounter = 1;
  98         String prefix;
  99         if (id == null) {
 100             prefix = baseNameOption.getValue(options);
 101             int slash = prefix.lastIndexOf(File.separatorChar);
 102             prefix = prefix.substring(slash + 1);
 103         } else {
 104             prefix = id;
 105         }
 106         for (;;) {
 107             int fileNameLengthWithoutLabel = uniqueTag.length() + ext.length() + prefix.length() + "[]".length();
 108             int labelLengthLimit = MAX_FILE_NAME_LENGTH - fileNameLengthWithoutLabel;
 109             String fileName;
 110             if (labelLengthLimit < ELLIPSIS.length()) {
 111                 // This means `id` is very long
 112                 String suffix = uniqueTag + ext;
 113                 int idLengthLimit = Math.min(MAX_FILE_NAME_LENGTH - suffix.length(), prefix.length());
 114                 fileName = sanitizeFileName(prefix.substring(0, idLengthLimit) + suffix);
 115             } else {
 116                 if (label == null) {
 117                     fileName = sanitizeFileName(prefix + uniqueTag + ext);
 118                 } else {
 119                     String adjustedLabel = label;
 120                     if (label.length() > labelLengthLimit) {
 121                         adjustedLabel = label.substring(0, labelLengthLimit - ELLIPSIS.length()) + ELLIPSIS;
 122                     }
 123                     fileName = sanitizeFileName(prefix + '[' + adjustedLabel + ']' + uniqueTag + ext);
 124                 }
 125             }
 126             Path dumpDir = DebugOptions.getDumpDirectory(options);
 127             Path result = Paths.get(dumpDir.toString(), fileName);
 128             try {
 129                 if (createDirectory) {
 130                     return Files.createDirectory(result);
 131                 } else {
 132                     return Files.createFile(result);
 133                 }
 134             } catch (FileAlreadyExistsException e) {
 135                 uniqueTag = "_" + dumpCounter++;
 136             }
 137         }
 138     }
 139 
 140 }
< prev index next >