Showing the changes to java/pkg/sun/tools/jmap/JMap.java (1 of 18352 files). Show all
Unified · Split
| @@ -0,0 +1,389 @@ | ||
| 1 | +/* | |
| 2 | + * Copyright (c) 2005, 2008, 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.tools.jmap; | |
| 27 | + | |
| 28 | +import java.lang.reflect.Method; | |
| 29 | +import java.io.File; | |
| 30 | +import java.io.IOException; | |
| 31 | +import java.io.InputStream; | |
| 32 | + | |
| 33 | +import com.sun.tools.attach.VirtualMachine; | |
| 34 | +import com.sun.tools.attach.AttachNotSupportedException; | |
| 35 | +import sun.tools.attach.HotSpotVirtualMachine; | |
| 36 | + | |
| 37 | +/* | |
| 38 | + * This class is the main class for the JMap utility. It parses its arguments | |
| 39 | + * and decides if the command should be satisifed using the VM attach mechanism | |
| 40 | + * or an SA tool. At this time the only option that uses the VM attach mechanism | |
| 41 | + * is the -dump option to get a heap dump of a running application. All other | |
| 42 | + * options are mapped to SA tools. | |
| 43 | + */ | |
| 44 | +public class JMap { | |
| 45 | + | |
| 46 | + // Options handled by the attach mechanism | |
| 47 | + private static String HISTO_OPTION = "-histo"; | |
| 48 | + private static String LIVE_HISTO_OPTION = "-histo:live"; | |
| 49 | + private static String DUMP_OPTION_PREFIX = "-dump:"; | |
| 50 | + | |
| 51 | + // These options imply the use of a SA tool | |
| 52 | + private static String SA_TOOL_OPTIONS = | |
| 53 | + "-heap|-heap:format=b|-permstat|-finalizerinfo"; | |
| 54 | + | |
| 55 | + // The -F (force) option is currently not passed through to SA | |
| 56 | + private static String FORCE_SA_OPTION = "-F"; | |
| 57 | + | |
| 58 | + // Default option (if nothing provided) | |
| 59 | + private static String DEFAULT_OPTION = "-pmap"; | |
| 60 | + | |
| 61 | + public static void main(String[] args) throws Exception { | |
| 62 | + if (args.length == 0) { | |
| 63 | + usage(); // no arguments | |
| 64 | + } | |
| 65 | + | |
| 66 | + // used to indicate if we should use SA | |
| 67 | + boolean useSA = false; | |
| 68 | + | |
| 69 | + // the chosen option (-heap, -dump:*, ... ) | |
| 70 | + String option = null; | |
| 71 | + | |
| 72 | + // First iterate over the options (arguments starting with -). There should be | |
| 73 | + // one (but maybe two if -F is also used). | |
| 74 | + int optionCount = 0; | |
| 75 | + while (optionCount < args.length) { | |
| 76 | + String arg = args[optionCount]; | |
| 77 | + if (!arg.startsWith("-")) { | |
| 78 | + break; | |
| 79 | + } | |
| 80 | + if (arg.equals(FORCE_SA_OPTION)) { | |
| 81 | + useSA = true; | |
| 82 | + } else { | |
| 83 | + if (option != null) { | |
| 84 | + usage(); // option already specified | |
| 85 | + } | |
| 86 | + option = arg; | |
| 87 | + } | |
| 88 | + optionCount++; | |
| 89 | + } | |
| 90 | + | |
| 91 | + // if no option provided then use default. | |
| 92 | + if (option == null) { | |
| 93 | + option = DEFAULT_OPTION; | |
| 94 | + } | |
| 95 | + if (option.matches(SA_TOOL_OPTIONS)) { | |
| 96 | + useSA = true; | |
| 97 | + } | |
| 98 | + | |
| 99 | + // Next we check the parameter count. For the SA tools there are | |
| 100 | + // one or two parameters. For the built-in -dump option there is | |
| 101 | + // only one parameter (the process-id) | |
| 102 | + int paramCount = args.length - optionCount; | |
| 103 | + if (paramCount == 0 || paramCount > 2) { | |
| 104 | + usage(); | |
| 105 | + } | |
| 106 | + | |
| 107 | + if (optionCount == 0 || paramCount != 1) { | |
| 108 | + useSA = true; | |
| 109 | + } else { | |
| 110 | + // the parameter for the -dump option is a process-id. | |
| 111 | + // If it doesn't parse to a number then it must be SA | |
| 112 | + // debug server | |
| 113 | + if (!args[optionCount].matches("[0-9]+")) { | |
| 114 | + useSA = true; | |
| 115 | + } | |
| 116 | + } | |
| 117 | + | |
| 118 | + | |
| 119 | + // at this point we know if we are executing an SA tool or a built-in | |
| 120 | + // option. | |
| 121 | + | |
| 122 | + if (useSA) { | |
| 123 | + // parameters (<pid> or <exe> <core>) | |
| 124 | + String params[] = new String[paramCount]; | |
| 125 | + for (int i=optionCount; i<args.length; i++ ){ | |
| 126 | + params[i-optionCount] = args[i]; | |
| 127 | + } | |
| 128 | + runTool(option, params); | |
| 129 | + | |
| 130 | + } else { | |
| 131 | + String pid = args[1]; | |
| 132 | + // Here we handle the built-in options | |
| 133 | + // As more options are added we should create an abstract tool class and | |
| 134 | + // have a table to map the options | |
| 135 | + if (option.equals(HISTO_OPTION)) { | |
| 136 | + histo(pid, false); | |
| 137 | + } else if (option.equals(LIVE_HISTO_OPTION)) { | |
| 138 | + histo(pid, true); | |
| 139 | + } else if (option.startsWith(DUMP_OPTION_PREFIX)) { | |
| 140 | + dump(pid, option); | |
| 141 | + } else { | |
| 142 | + usage(); | |
| 143 | + } | |
| 144 | + } | |
| 145 | + } | |
| 146 | + | |
| 147 | + // Invoke SA tool with the given arguments | |
| 148 | + private static void runTool(String option, String args[]) throws Exception { | |
| 149 | + String[][] tools = { | |
| 150 | + { "-pmap", "sun.jvm.hotspot.tools.PMap" }, | |
| 151 | + { "-heap", "sun.jvm.hotspot.tools.HeapSummary" }, | |
| 152 | + { "-heap:format=b", "sun.jvm.hotspot.tools.HeapDumper" }, | |
| 153 | + { "-histo", "sun.jvm.hotspot.tools.ObjectHistogram" }, | |
| 154 | + { "-permstat", "sun.jvm.hotspot.tools.PermStat" }, | |
| 155 | + { "-finalizerinfo", "sun.jvm.hotspot.tools.FinalizerInfo" }, | |
| 156 | + }; | |
| 157 | + | |
| 158 | + String tool = null; | |
| 159 | + | |
| 160 | + // -dump option needs to be handled in a special way | |
| 161 | + if (option.startsWith(DUMP_OPTION_PREFIX)) { | |
| 162 | + // first check that the option can be parsed | |
| 163 | + String fn = parseDumpOptions(option); | |
| 164 | + if (fn == null) usage(); | |
| 165 | + | |
| 166 | + // tool for heap dumping | |
| 167 | + tool = "sun.jvm.hotspot.tools.HeapDumper"; | |
| 168 | + | |
| 169 | + // HeapDumper -f <file> | |
| 170 | + args = prepend(fn, args); | |
| 171 | + args = prepend("-f", args); | |
| 172 | + } else { | |
| 173 | + int i=0; | |
| 174 | + while (i < tools.length) { | |
| 175 | + if (option.equals(tools[i][0])) { | |
| 176 | + tool = tools[i][1]; | |
| 177 | + break; | |
| 178 | + } | |
| 179 | + i++; | |
| 180 | + } | |
| 181 | + } | |
| 182 | + if (tool == null) { | |
| 183 | + usage(); // no mapping to tool | |
| 184 | + } | |
| 185 | + | |
| 186 | + // Tool not available on this platform. | |
| 187 | + Class<?> c = loadClass(tool); | |
| 188 | + if (c == null) { | |
| 189 | + usage(); | |
| 190 | + } | |
| 191 | + | |
| 192 | + // invoke the main method with the arguments | |
| 193 | + Class[] argTypes = { String[].class } ; | |
| 194 | + Method m = c.getDeclaredMethod("main", argTypes); | |
| 195 | + | |
| 196 | + Object[] invokeArgs = { args }; | |
| 197 | + m.invoke(null, invokeArgs); | |
| 198 | + } | |
| 199 | + | |
| 200 | + // loads the given class using the system class loader | |
| 201 | + private static Class loadClass(String name) { | |
| 202 | + // | |
| 203 | + // We specify the system clas loader so as to cater for development | |
| 204 | + // environments where this class is on the boot class path but sa-jdi.jar | |
| 205 | + // is on the system class path. Once the JDK is deployed then both | |
| 206 | + // tools.jar and sa-jdi.jar are on the system class path. | |
| 207 | + // | |
| 208 | + try { | |
| 209 | + return Class.forName(name, true, | |
| 210 | + ClassLoader.getSystemClassLoader()); | |
| 211 | + } catch (Exception x) { } | |
| 212 | + return null; | |
| 213 | + } | |
| 214 | + | |
| 215 | + private static final String LIVE_OBJECTS_OPTION = "-live"; | |
| 216 | + private static final String ALL_OBJECTS_OPTION = "-all"; | |
| 217 | + private static void histo(String pid, boolean live) throws IOException { | |
| 218 | + VirtualMachine vm = attach(pid); | |
| 219 | + InputStream in = ((HotSpotVirtualMachine)vm). | |
| 220 | + heapHisto(live ? LIVE_OBJECTS_OPTION : ALL_OBJECTS_OPTION); | |
| 221 | + drain(vm, in); | |
| 222 | + } | |
| 223 | + | |
| 224 | + private static void dump(String pid, String options) throws IOException { | |
| 225 | + // parse the options to get the dump filename | |
| 226 | + String filename = parseDumpOptions(options); | |
| 227 | + if (filename == null) { | |
| 228 | + usage(); // invalid options or no filename | |
| 229 | + } | |
| 230 | + | |
| 231 | + // get the canonical path - important to avoid just passing | |
| 232 | + // a "heap.bin" and having the dump created in the target VM | |
| 233 | + // working directory rather than the directory where jmap | |
| 234 | + // is executed. | |
| 235 | + filename = new File(filename).getCanonicalPath(); | |
| 236 | + | |
| 237 | + // dump live objects only or not | |
| 238 | + boolean live = isDumpLiveObjects(options); | |
| 239 | + | |
| 240 | + VirtualMachine vm = attach(pid); | |
| 241 | + System.out.println("Dumping heap to " + filename + " ..."); | |
| 242 | + InputStream in = ((HotSpotVirtualMachine)vm). | |
| 243 | + dumpHeap((Object)filename, | |
| 244 | + (live ? LIVE_OBJECTS_OPTION : ALL_OBJECTS_OPTION)); | |
| 245 | + drain(vm, in); | |
| 246 | + } | |
| 247 | + | |
| 248 | + // Parse the options to the -dump option. Valid options are format=b and | |
| 249 | + // file=<file>. Returns <file> if provided. Returns null if <file> not | |
| 250 | + // provided, or invalid option. | |
| 251 | + private static String parseDumpOptions(String arg) { | |
| 252 | + assert arg.startsWith(DUMP_OPTION_PREFIX); | |
| 253 | + | |
| 254 | + String filename = null; | |
| 255 | + | |
| 256 | + // options are separated by comma (,) | |
| 257 | + String options[] = arg.substring(DUMP_OPTION_PREFIX.length()).split(","); | |
| 258 | + | |
| 259 | + for (int i=0; i<options.length; i++) { | |
| 260 | + String option = options[i]; | |
| 261 | + | |
| 262 | + if (option.equals("format=b")) { | |
| 263 | + // ignore format (not needed at this time) | |
| 264 | + } else if (option.equals("live")) { | |
| 265 | + // a valid suboption | |
| 266 | + } else { | |
| 267 | + | |
| 268 | + // file=<file> - check that <file> is specified | |
| 269 | + if (option.startsWith("file=")) { | |
| 270 | + filename = option.substring(5); | |
| 271 | + if (filename.length() == 0) { | |
| 272 | + return null; | |
| 273 | + } | |
| 274 | + } else { | |
| 275 | + return null; // option not recognized | |
| 276 | + } | |
| 277 | + } | |
| 278 | + } | |
| 279 | + return filename; | |
| 280 | + } | |
| 281 | + | |
| 282 | + private static boolean isDumpLiveObjects(String arg) { | |
| 283 | + // options are separated by comma (,) | |
| 284 | + String options[] = arg.substring(DUMP_OPTION_PREFIX.length()).split(","); | |
| 285 | + for (String suboption : options) { | |
| 286 | + if (suboption.equals("live")) { | |
| 287 | + return true; | |
| 288 | + } | |
| 289 | + } | |
| 290 | + return false; | |
| 291 | + } | |
| 292 | + | |
| 293 | + // Attach to <pid>, existing if we fail to attach | |
| 294 | + private static VirtualMachine attach(String pid) { | |
| 295 | + try { | |
| 296 | + return VirtualMachine.attach(pid); | |
| 297 | + } catch (Exception x) { | |
| 298 | + String msg = x.getMessage(); | |
| 299 | + if (msg != null) { | |
| 300 | + System.err.println(pid + ": " + msg); | |
| 301 | + } else { | |
| 302 | + x.printStackTrace(); | |
| 303 | + } | |
| 304 | + if ((x instanceof AttachNotSupportedException) && haveSA()) { | |
| 305 | + System.err.println("The -F option can be used when the " + | |
| 306 | + "target process is not responding"); | |
| 307 | + } | |
| 308 | + System.exit(1); | |
| 309 | + return null; // keep compiler happy | |
| 310 | + } | |
| 311 | + } | |
| 312 | + | |
| 313 | + // Read the stream from the target VM until EOF, then detach | |
| 314 | + private static void drain(VirtualMachine vm, InputStream in) throws IOException { | |
| 315 | + // read to EOF and just print output | |
| 316 | + byte b[] = new byte[256]; | |
| 317 | + int n; | |
| 318 | + do { | |
| 319 | + n = in.read(b); | |
| 320 | + if (n > 0) { | |
| 321 | + String s = new String(b, 0, n, "UTF-8"); | |
| 322 | + System.out.print(s); | |
| 323 | + } | |
| 324 | + } while (n > 0); | |
| 325 | + in.close(); | |
| 326 | + vm.detach(); | |
| 327 | + } | |
| 328 | + | |
| 329 | + // return a new string array with arg as the first element | |
| 330 | + private static String[] prepend(String arg, String args[]) { | |
| 331 | + String[] newargs = new String[args.length+1]; | |
| 332 | + newargs[0] = arg; | |
| 333 | + System.arraycopy(args, 0, newargs, 1, args.length); | |
| 334 | + return newargs; | |
| 335 | + } | |
| 336 | + | |
| 337 | + // returns true if SA is available | |
| 338 | + private static boolean haveSA() { | |
| 339 | + Class c = loadClass("sun.jvm.hotspot.tools.HeapSummary"); | |
| 340 | + return (c != null); | |
| 341 | + } | |
| 342 | + | |
| 343 | + // print usage message | |
| 344 | + private static void usage() { | |
| 345 | + System.out.println("Usage:"); | |
| 346 | + if (haveSA()) { | |
| 347 | + System.out.println(" jmap [option] <pid>"); | |
| 348 | + System.out.println(" (to connect to running process)"); | |
| 349 | + System.out.println(" jmap [option] <executable <core>"); | |
| 350 | + System.out.println(" (to connect to a core file)"); | |
| 351 | + System.out.println(" jmap [option] [server_id@]<remote server IP or hostname>"); | |
| 352 | + System.out.println(" (to connect to remote debug server)"); | |
| 353 | + System.out.println(""); | |
| 354 | + System.out.println("where <option> is one of:"); | |
| 355 | + System.out.println(" <none> to print same info as Solaris pmap"); | |
| 356 | + System.out.println(" -heap to print java heap summary"); | |
| 357 | + System.out.println(" -histo[:live] to print histogram of java object heap; if the \"live\""); | |
| 358 | + System.out.println(" suboption is specified, only count live objects"); | |
| 359 | + System.out.println(" -permstat to print permanent generation statistics"); | |
| 360 | + System.out.println(" -finalizerinfo to print information on objects awaiting finalization"); | |
| 361 | + System.out.println(" -dump:<dump-options> to dump java heap in hprof binary format"); | |
| 362 | + System.out.println(" dump-options:"); | |
| 363 | + System.out.println(" live dump only live objects; if not specified,"); | |
| 364 | + System.out.println(" all objects in the heap are dumped."); | |
| 365 | + System.out.println(" format=b binary format"); | |
| 366 | + System.out.println(" file=<file> dump heap to <file>"); | |
| 367 | + System.out.println(" Example: jmap -dump:live,format=b,file=heap.bin <pid>"); | |
| 368 | + System.out.println(" -F force. Use with -dump:<dump-options> <pid> or -histo"); | |
| 369 | + System.out.println(" to force a heap dump or histogram when <pid> does not"); | |
| 370 | + System.out.println(" respond. The \"live\" suboption is not supported"); | |
| 371 | + System.out.println(" in this mode."); | |
| 372 | + System.out.println(" -h | -help to print this help message"); | |
| 373 | + System.out.println(" -J<flag> to pass <flag> directly to the runtime system"); | |
| 374 | + } else { | |
| 375 | + System.out.println(" jmap -histo <pid>"); | |
| 376 | + System.out.println(" (to connect to running process and print histogram of java object heap"); | |
| 377 | + System.out.println(" jmap -dump:<dump-options> <pid>"); | |
| 378 | + System.out.println(" (to connect to running process and dump java heap)"); | |
| 379 | + System.out.println(""); | |
| 380 | + System.out.println(" dump-options:"); | |
| 381 | + System.out.println(" format=b binary default"); | |
| 382 | + System.out.println(" file=<file> dump heap to <file>"); | |
| 383 | + System.out.println(""); | |
| 384 | + System.out.println(" Example: jmap -dump:format=b,file=heap.bin <pid>"); | |
| 385 | + } | |
| 386 | + | |
| 387 | + System.exit(1); | |
| 388 | + } | |
| 389 | +} | |