1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ahat; 18 19 import com.android.ahat.heapdump.AhatSnapshot; 20 import com.android.ahat.heapdump.Diff; 21 import com.android.ahat.heapdump.HprofFormatException; 22 import com.android.ahat.heapdump.Parser; 23 import com.android.ahat.heapdump.Reachability; 24 import com.android.ahat.progress.Progress; 25 import com.android.ahat.proguard.ProguardMap; 26 import com.sun.net.httpserver.HttpServer; 27 import java.io.File; 28 import java.io.IOException; 29 import java.io.PrintStream; 30 import java.net.InetAddress; 31 import java.net.InetSocketAddress; 32 import java.text.ParseException; 33 import java.util.concurrent.Executors; 34 35 /** 36 * Contains the main entry point for the ahat heap dump viewer. 37 */ 38 public class Main { Main()39 private Main() { 40 } 41 help(PrintStream out)42 private static void help(PrintStream out) { 43 out.println("java -jar ahat.jar [OPTIONS] FILE"); 44 out.println(" Launch an http server for viewing the given Android heap dump FILE."); 45 out.println(""); 46 out.println("OPTIONS:"); 47 out.println(" -p <port>"); 48 out.println(" Serve pages on the given port. Defaults to 7100."); 49 out.println(" --proguard-map FILE"); 50 out.println(" Use the proguard map FILE to deobfuscate the heap dump."); 51 out.println(" --baseline FILE"); 52 out.println(" Diff the heap dump against the given baseline heap dump FILE."); 53 out.println(" --baseline-proguard-map FILE"); 54 out.println(" Use the proguard map FILE to deobfuscate the baseline heap dump."); 55 out.println(" --retained [strong | soft | finalizer | weak | phantom | unreachable]"); 56 out.println(" The weakest reachability of instances to treat as retained."); 57 out.println(" Defaults to soft"); 58 out.println(""); 59 } 60 61 /** 62 * Load the given heap dump file. 63 * Prints an error message and exits the application on failure to load the 64 * heap dump. 65 */ loadHeapDump(File hprof, ProguardMap map, Progress progress, Reachability retained)66 private static AhatSnapshot loadHeapDump(File hprof, 67 ProguardMap map, Progress progress, Reachability retained) { 68 System.out.println("Processing '" + hprof + "' ..."); 69 try { 70 return new Parser(hprof).map(map).progress(progress).retained(retained).parse(); 71 } catch (IOException e) { 72 System.err.println("Unable to load '" + hprof + "':"); 73 e.printStackTrace(); 74 } catch (HprofFormatException e) { 75 System.err.println("'" + hprof + "' does not appear to be a valid Java heap dump:"); 76 e.printStackTrace(); 77 } 78 System.exit(1); 79 throw new AssertionError("Unreachable"); 80 } 81 82 /** 83 * Main entry for ahat heap dump viewer. 84 * Launches an http server on localhost for viewing a given heap dump. 85 * See the ahat README or pass "--help" as one of the arguments to see a 86 * description of what arguments and options are expected. 87 * 88 * @param args the command line arguments 89 */ main(String[] args)90 public static void main(String[] args) { 91 int port = 7100; 92 for (String arg : args) { 93 if (arg.equals("--help")) { 94 help(System.out); 95 return; 96 } 97 } 98 99 File hprof = null; 100 File hprofbase = null; 101 ProguardMap map = new ProguardMap(); 102 ProguardMap mapbase = new ProguardMap(); 103 Reachability retained = Reachability.SOFT; 104 for (int i = 0; i < args.length; i++) { 105 if ("-p".equals(args[i]) && i + 1 < args.length) { 106 i++; 107 port = Integer.parseInt(args[i]); 108 } else if ("--proguard-map".equals(args[i]) && i + 1 < args.length) { 109 i++; 110 try { 111 map.readFromFile(new File(args[i])); 112 } catch (IOException | ParseException ex) { 113 System.out.println("Unable to read proguard map: " + ex); 114 System.out.println("The proguard map will not be used."); 115 } 116 } else if ("--baseline-proguard-map".equals(args[i]) && i + 1 < args.length) { 117 i++; 118 try { 119 mapbase.readFromFile(new File(args[i])); 120 } catch (IOException | ParseException ex) { 121 System.out.println("Unable to read baseline proguard map: " + ex); 122 System.out.println("The proguard map will not be used."); 123 } 124 } else if ("--baseline".equals(args[i]) && i + 1 < args.length) { 125 i++; 126 if (hprofbase != null) { 127 System.err.println("multiple baseline heap dumps."); 128 help(System.err); 129 return; 130 } 131 hprofbase = new File(args[i]); 132 } else if ("--retained".equals(args[i]) && i + 1 < args.length) { 133 i++; 134 switch (args[i]) { 135 case "strong": retained = Reachability.STRONG; break; 136 case "soft": retained = Reachability.SOFT; break; 137 case "finalizer": retained = Reachability.FINALIZER; break; 138 case "weak": retained = Reachability.WEAK; break; 139 case "phantom": retained = Reachability.PHANTOM; break; 140 case "unreachable": retained = Reachability.UNREACHABLE; break; 141 default: 142 System.err.println("Invalid retained reference type: " + args[i]); 143 help(System.err); 144 return; 145 } 146 } else { 147 if (hprof != null) { 148 System.err.println("multiple input files."); 149 help(System.err); 150 return; 151 } 152 hprof = new File(args[i]); 153 } 154 } 155 156 if (hprof == null) { 157 System.err.println("no input file."); 158 help(System.err); 159 return; 160 } 161 162 // Launch the server before parsing the hprof file so we get 163 // BindExceptions quickly. 164 InetAddress loopback = InetAddress.getLoopbackAddress(); 165 InetSocketAddress addr = new InetSocketAddress(loopback, port); 166 System.out.println("Preparing " + addr + " ..."); 167 HttpServer server = null; 168 try { 169 server = HttpServer.create(addr, 0); 170 } catch (IOException e) { 171 System.err.println("Unable to setup ahat server:"); 172 e.printStackTrace(); 173 System.exit(1); 174 } 175 176 AhatSnapshot ahat = loadHeapDump(hprof, map, new AsciiProgress(), retained); 177 if (hprofbase != null) { 178 AhatSnapshot base = loadHeapDump(hprofbase, mapbase, new AsciiProgress(), retained); 179 180 System.out.println("Diffing heap dumps ..."); 181 Diff.snapshots(ahat, base); 182 } 183 184 server.createContext("/", 185 new AhatHttpHandler(new OverviewHandler(ahat, hprof, hprofbase, retained))); 186 server.createContext("/rooted", new AhatHttpHandler(new RootedHandler(ahat))); 187 server.createContext("/object", new AhatHttpHandler(new ObjectHandler(ahat))); 188 server.createContext("/objects", new AhatHttpHandler(new ObjectsHandler(ahat))); 189 server.createContext("/site", new AhatHttpHandler(new SiteHandler(ahat))); 190 server.createContext("/bitmap", new BitmapHandler(ahat)); 191 server.createContext("/style.css", new StaticHandler("etc/style.css", "text/css")); 192 server.setExecutor(Executors.newFixedThreadPool(1)); 193 System.out.println("Server started on http://localhost:" + port); 194 195 server.start(); 196 } 197 } 198 199