1 /* 2 * Copyright (c) 2004, 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 26 package sun.jvmstat.perfdata.monitor.protocol.local; 27 28 import java.io.File; 29 import java.io.FilenameFilter; 30 import jdk.internal.vm.VMSupport; 31 32 /** 33 * Class to provide translations from the local Vm Identifier 34 * name space into the file system name space and vice-versa. 35 * <p> 36 * Provides a factory for creating a File object to the backing 37 * store file for instrumentation shared memory region for a JVM 38 * identified by its Local Java Virtual Machine Identifier, or 39 * <em>lvmid</em>. 40 * 41 * @author Brian Doherty 42 * @since 1.5 43 * @see java.io.File 44 */ 45 public class PerfDataFile { 46 private PerfDataFile() { }; 47 48 /** 49 * The name of the of the system dependent temporary directory 50 */ 51 public static final String tmpDirName; 52 53 /** 54 * The file name prefix for PerfData shared memory files. 55 * <p> 56 * This prefix must be kept in sync with the prefix used by the JVM. 57 */ 58 public static final String dirNamePrefix = "hsperfdata_"; 59 60 /** 61 * The directory name pattern for the user directories. 62 */ 63 public static final String userDirNamePattern = "hsperfdata_\\S*"; 64 65 /** 66 * The file name pattern for PerfData shared memory files. 67 * <p> 68 * This pattern must be kept in synch with the file name pattern 69 * used by the 1.4.2 and later HotSpot JVM. 70 */ 71 public static final String fileNamePattern = "^[0-9]+$"; 72 73 /** 74 * The file name pattern for 1.4.1 PerfData shared memory files. 75 * <p> 76 * This pattern must be kept in synch with the file name pattern 77 * used by the 1.4.1 HotSpot JVM. 78 */ 79 public static final String tmpFileNamePattern = 80 "^hsperfdata_[0-9]+(_[1-2]+)?$"; 81 82 83 /** 84 * Get a File object for the instrumentation backing store file 85 * for the JVM identified by the given local Vm Identifier. 86 * <p> 87 * This method looks for the most up to date backing store file for 88 * the given {@code lvmid}. It will search all the user specific 89 * directories in the temporary directory for the host operating 90 * system, which may be influenced by platform specific environment 91 * variables. 92 * 93 * @param lvmid the local Java Virtual Machine Identifier for the target 94 * @return File - a File object to the backing store file for the named 95 * shared memory region of the target JVM. 96 * @see java.io.File 97 * @see #getTempDirectory() 98 */ 99 public static File getFile(int lvmid) { 100 if (lvmid == 0) { 101 /* 102 * lvmid == 0 is used to indicate the current Java Virtual Machine. 103 * If the SDK provided an API to get a unique Java Virtual Machine 104 * identifier, then a filename could be constructed with that 105 * identifier. In absence of such an api, return null. 106 */ 107 return null; 108 } 109 110 /* 111 * iterate over all files in all directories in tmpDirName that 112 * match the file name patterns. 113 */ 114 File tmpDir = new File(tmpDirName); 115 String[] files = tmpDir.list(new FilenameFilter() { 116 public boolean accept(File dir, String name) { 117 if (!name.startsWith(dirNamePrefix)) { 118 return false; 119 } 120 File candidate = new File(dir, name); 121 return ((candidate.isDirectory() || candidate.isFile()) 122 && candidate.canRead()); 123 } 124 }); 125 126 long newestTime = 0; 127 File newest = null; 128 129 for (int i = 0; i < files.length; i++) { 130 File f = new File(tmpDirName + files[i]); 131 File candidate = null; 132 133 if (f.exists() && f.isDirectory()) { 134 /* 135 * found a directory matching the name patterns. This 136 * is a 1.4.2 hsperfdata_<user> directory. Check for 137 * file named <lvmid> in that directory 138 */ 139 String name = Integer.toString(lvmid); 140 candidate = new File(f.getName(), name); 141 142 } else if (f.exists() && f.isFile()) { 143 /* 144 * found a file matching the name patterns. This 145 * is a 1.4.1 hsperfdata_<lvmid> file. 146 */ 147 candidate = f; 148 149 } else { 150 // unexpected - let conditional below filter this one out 151 candidate = f; 152 } 153 154 if (candidate.exists() && candidate.isFile() 155 && candidate.canRead()) { 156 long modTime = candidate.lastModified(); 157 if (modTime >= newestTime) { 158 newestTime = modTime; 159 newest = candidate; 160 } 161 } 162 } 163 return newest; 164 } 165 166 /** 167 * Return the File object for the backing store file for the specified Java 168 * Virtual Machine. 169 * <p> 170 * This method looks for the most up to date backing store file for 171 * the JVM identified by the given user name and lvmid. The directory 172 * searched is the temporary directory for the host operating system, 173 * which may be influenced by environment variables. 174 * 175 * @param user the user name 176 * @param lvmid the local Java Virtual Machine Identifier for the target 177 * @return File - a File object to the backing store file for the named 178 * shared memory region of the target JVM. 179 * @see java.io.File 180 * @see #getTempDirectory() 181 */ 182 public static File getFile(String user, int lvmid) { 183 if (lvmid == 0) { 184 /* 185 * lvmid == 0 is used to indicate the current Java Virtual Machine. 186 * If the SDK provided an API to get a unique Java Virtual Machine 187 * identifier, then a filename could be constructed with that 188 * identifier. In absence of such an api, return null. 189 */ 190 return null; 191 } 192 193 // first try for 1.4.2 and later JVMs 194 String basename = getTempDirectory(user) + Integer.toString(lvmid); 195 File f = new File(basename); 196 197 if (f.exists() && f.isFile() && f.canRead()) { 198 return f; 199 } 200 201 // No hit on 1.4.2 JVMs, try 1.4.1 files 202 long newestTime = 0; 203 File newest = null; 204 for (int i = 0; i < 2; i++) { 205 if (i == 0) { 206 basename = getTempDirectory() + Integer.toString(lvmid); 207 } else { 208 basename = getTempDirectory() + Integer.toString(lvmid) 209 + Integer.toString(i); 210 } 211 212 f = new File(basename); 213 214 if (f.exists() && f.isFile() && f.canRead()) { 215 long modTime = f.lastModified(); 216 if (modTime >= newestTime) { 217 newestTime = modTime; 218 newest = f; 219 } 220 } 221 } 222 return newest; 223 } 224 225 /** 226 * Method to extract a local Java Virtual Machine Identifier from the 227 * file name of the given File object. 228 * 229 * @param file A File object representing the name of a 230 * shared memory region for a target JVM 231 * @return int - the local Java Virtual Machine Identifier for the target 232 * associated with the file 233 * @throws java.lang.IllegalArgumentException Thrown if the file name 234 * does not conform to the expected pattern 235 */ 236 public static int getLocalVmId(File file) { 237 try { 238 // try 1.4.2 and later format first 239 return Integer.parseInt(file.getName()); 240 } catch (NumberFormatException e) { } 241 242 // now try the 1.4.1 format 243 String name = file.getName(); 244 if (name.startsWith(dirNamePrefix)) { 245 int first = name.indexOf('_'); 246 int last = name.lastIndexOf('_'); 247 try { 248 if (first == last) { 249 return Integer.parseInt(name.substring(first + 1)); 250 } else { 251 return Integer.parseInt(name.substring(first + 1, last)); 252 } 253 } catch (NumberFormatException e) { } 254 } 255 throw new IllegalArgumentException("file name does not match pattern"); 256 } 257 258 /** 259 * Return the name of the temporary directory being searched for 260 * HotSpot PerfData backing store files. 261 * <p> 262 * This method generally returns the value of the java.io.tmpdir 263 * property. However, on some platforms it may return a different 264 * directory, as the JVM implementation may store the PerfData backing 265 * store files in a different directory for performance reasons. 266 * 267 * @return String - the name of the temporary directory. 268 */ 269 public static String getTempDirectory() { 270 return tmpDirName; 271 } 272 273 /** 274 * Return the name of the temporary directory to be searched 275 * for HotSpot PerfData backing store files for a given user. 276 * <p> 277 * This method generally returns the name of a subdirectory of 278 * the directory indicated in the java.io.tmpdir property. However, 279 * on some platforms it may return a different directory, as the 280 * JVM implementation may store the PerfData backing store files 281 * in a different directory for performance reasons. 282 * 283 * @return String - the name of the temporary directory. 284 */ 285 public static String getTempDirectory(String user) { 286 return tmpDirName + dirNamePrefix + user + File.separator; 287 } 288 289 static { 290 /* 291 * For this to work, the target VM and this code need to use 292 * the same directory. Instead of guessing which directory the 293 * VM is using, we will ask. 294 */ 295 String tmpdir = VMSupport.getVMTemporaryDirectory(); 296 297 /* 298 * Assure that the string returned has a trailing File.separator 299 * character. This check was added because the Linux implementation 300 * changed such that the java.io.tmpdir string no longer terminates 301 * with a File.separator character. 302 */ 303 if (tmpdir.lastIndexOf(File.separator) != (tmpdir.length()-1)) { 304 tmpdir = tmpdir + File.separator; 305 } 306 tmpDirName = tmpdir; 307 } 308 }