1 /*
   2  * Copyright (c) 2016, 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.
   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.options;
  24 
  25 import java.nio.file.Path;
  26 import java.nio.file.Paths;
  27 import java.util.HashMap;
  28 import java.util.concurrent.atomic.AtomicInteger;
  29 import java.util.concurrent.atomic.AtomicLong;
  30 
  31 public class UniquePathUtilities {
  32 
  33     private static final AtomicLong globalTimeStamp = new AtomicLong();
  34     /**
  35      * This generates a per thread persistent id to aid mapping related dump files with each other.
  36      */
  37     private static final ThreadLocal<PerThreadSequence> threadDumpId = new ThreadLocal<>();
  38     private static final AtomicInteger dumpId = new AtomicInteger();
  39 
  40     static class PerThreadSequence {
  41         final int threadID;
  42         HashMap<String, Integer> sequences = new HashMap<>(2);
  43 
  44         PerThreadSequence(int threadID) {
  45             this.threadID = threadID;
  46         }
  47 
  48         String generateID(String extension) {
  49             Integer box = sequences.get(extension);
  50             if (box == null) {
  51                 sequences.put(extension, 1);
  52                 return Integer.toString(threadID);
  53             } else {
  54                 sequences.put(extension, box + 1);
  55                 return Integer.toString(threadID) + '-' + box;
  56             }
  57         }
  58     }
  59 
  60     private static String getThreadDumpId(String extension) {
  61         PerThreadSequence id = threadDumpId.get();
  62         if (id == null) {
  63             id = new PerThreadSequence(dumpId.incrementAndGet());
  64             threadDumpId.set(id);
  65         }
  66         return id.generateID(extension);
  67     }
  68 
  69     private static String formatExtension(String ext) {
  70         if (ext == null || ext.length() == 0) {
  71             return "";
  72         }
  73         return "." + ext;
  74     }
  75 
  76     public static long getGlobalTimeStamp() {
  77         if (globalTimeStamp.get() == 0) {
  78             globalTimeStamp.compareAndSet(0, System.currentTimeMillis());
  79         }
  80         return globalTimeStamp.get();
  81     }
  82 
  83     /**
  84      * Generates a {@link Path} using the format "%s-%d_%d%s" with the {@link OptionKey#getValue
  85      * base filename}, a {@link #globalTimeStamp global timestamp} , {@link #getThreadDumpId a per
  86      * thread unique id} and an optional {@code extension}.
  87      *
  88      * @return the output file path or null if the flag is null
  89      */
  90     public static Path getPath(OptionValues options, OptionKey<String> option, OptionKey<String> defaultDirectory, String extension) {
  91         return getPath(options, option, defaultDirectory, extension, true);
  92     }
  93 
  94     /**
  95      * Generate a {@link Path} using the format "%s-%d_%s" with the {@link OptionKey#getValue base
  96      * filename}, a {@link #globalTimeStamp global timestamp} and an optional {@code extension} .
  97      *
  98      * @return the output file path or null if the flag is null
  99      */
 100     public static Path getPathGlobal(OptionValues options, OptionKey<String> option, OptionKey<String> defaultDirectory, String extension) {
 101         return getPath(options, option, defaultDirectory, extension, false);
 102     }
 103 
 104     private static Path getPath(OptionValues options, OptionKey<String> option, OptionKey<String> defaultDirectory, String extension, boolean includeThreadId) {
 105         if (option.getValue(options) == null) {
 106             return null;
 107         }
 108         String ext = formatExtension(extension);
 109         final String name = includeThreadId
 110                         ? String.format("%s-%d_%s%s", option.getValue(options), getGlobalTimeStamp(), getThreadDumpId(ext), ext)
 111                         : String.format("%s-%d%s", option.getValue(options), getGlobalTimeStamp(), ext);
 112         Path result = Paths.get(name);
 113         if (result.isAbsolute() || defaultDirectory == null) {
 114             return result;
 115         }
 116         return Paths.get(defaultDirectory.getValue(options), name).normalize();
 117     }
 118 }