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.proguard.ProguardMap; 24 import com.sun.net.httpserver.HttpServer; 25 import java.io.File; 26 import java.io.IOException; 27 import java.io.PrintStream; 28 import java.net.InetAddress; 29 import java.net.InetSocketAddress; 30 import java.text.ParseException; 31 import java.util.concurrent.Executors; 32 33 /** 34 * Contains the main entry point for the ahat heap dump viewer. 35 */ 36 public class Main { 37 private Main() { 38 } 39 40 private static void help(PrintStream out) { 41 out.println("java -jar ahat.jar [OPTIONS] FILE"); 42 out.println(" Launch an http server for viewing the given Android heap dump FILE."); 43 out.println(""); 44 out.println("OPTIONS:"); 45 out.println(" -p <port>"); 46 out.println(" Serve pages on the given port. Defaults to 7100."); 47 out.println(" --proguard-map FILE"); 48 out.println(" Use the proguard map FILE to deobfuscate the heap dump."); 49 out.println(" --baseline FILE"); 50 out.println(" Diff the heap dump against the given baseline heap dump FILE."); 51 out.println(" --baseline-proguard-map FILE"); 52 out.println(" Use the proguard map FILE to deobfuscate the baseline heap dump."); 53 out.println(""); 54 } 55 56 /** 57 * Load the given heap dump file. 58 * Prints an error message and exits the application on failure to load the 59 * heap dump. 60 */ 61 private static AhatSnapshot loadHeapDump(File hprof, ProguardMap map) { 62 System.out.println("Processing '" + hprof + "' ..."); 63 try { 64 return Parser.parseHeapDump(hprof, map); 65 } catch (IOException e) { 66 System.err.println("Unable to load '" + hprof + "':"); 67 e.printStackTrace(); 68 } catch (HprofFormatException e) { 69 System.err.println("'" + hprof + "' does not appear to be a valid Java heap dump:"); 70 e.printStackTrace(); 71 } 72 System.exit(1); 73 throw new AssertionError("Unreachable"); 74 } 75 76 /** 77 * Main entry for ahat heap dump viewer. 78 * Launches an http server on localhost for viewing a given heap dump. 79 * See the ahat README or pass "--help" as one of the arguments to see a 80 * description of what arguments and options are expected. 81 * 82 * @param args the command line arguments 83 */ 84 public static void main(String[] args) { 85 int port = 7100; 86 for (String arg : args) { 87 if (arg.equals("--help")) { 88 help(System.out); 89 return; 90 } 91 } 92 93 File hprof = null; 94 File hprofbase = null; 95 ProguardMap map = new ProguardMap(); 96 ProguardMap mapbase = new ProguardMap(); 97 for (int i = 0; i < args.length; i++) { 98 if ("-p".equals(args[i]) && i + 1 < args.length) { 99 i++; 100 port = Integer.parseInt(args[i]); 101 } else if ("--proguard-map".equals(args[i]) && i + 1 < args.length) { 102 i++; 103 try { 104 map.readFromFile(new File(args[i])); 105 } catch (IOException|ParseException ex) { 106 System.out.println("Unable to read proguard map: " + ex); 107 System.out.println("The proguard map will not be used."); 108 } 109 } else if ("--baseline-proguard-map".equals(args[i]) && i + 1 < args.length) { 110 i++; 111 try { 112 mapbase.readFromFile(new File(args[i])); 113 } catch (IOException|ParseException ex) { 114 System.out.println("Unable to read baseline proguard map: " + ex); 115 System.out.println("The proguard map will not be used."); 116 } 117 } else if ("--baseline".equals(args[i]) && i + 1 < args.length) { 118 i++; 119 if (hprofbase != null) { 120 System.err.println("multiple baseline heap dumps."); 121 help(System.err); 122 return; 123 } 124 hprofbase = new File(args[i]); 125 } else { 126 if (hprof != null) { 127 System.err.println("multiple input files."); 128 help(System.err); 129 return; 130 } 131 hprof = new File(args[i]); 132 } 133 } 134 135 if (hprof == null) { 136 System.err.println("no input file."); 137 help(System.err); 138 return; 139 } 140 141 // Launch the server before parsing the hprof file so we get 142 // BindExceptions quickly. 143 InetAddress loopback = InetAddress.getLoopbackAddress(); 144 InetSocketAddress addr = new InetSocketAddress(loopback, port); 145 System.out.println("Preparing " + addr + " ..."); 146 HttpServer server = null; 147 try { 148 server = HttpServer.create(addr, 0); 149 } catch (IOException e) { 150 System.err.println("Unable to setup ahat server:"); 151 e.printStackTrace(); 152 System.exit(1); 153 } 154 155 AhatSnapshot ahat = loadHeapDump(hprof, map); 156 if (hprofbase != null) { 157 AhatSnapshot base = loadHeapDump(hprofbase, mapbase); 158 159 System.out.println("Diffing heap dumps ..."); 160 Diff.snapshots(ahat, base); 161 } 162 163 server.createContext("/", new AhatHttpHandler(new OverviewHandler(ahat, hprof, hprofbase))); 164 server.createContext("/rooted", new AhatHttpHandler(new RootedHandler(ahat))); 165 server.createContext("/object", new AhatHttpHandler(new ObjectHandler(ahat))); 166 server.createContext("/objects", new AhatHttpHandler(new ObjectsHandler(ahat))); 167 server.createContext("/site", new AhatHttpHandler(new SiteHandler(ahat))); 168 server.createContext("/bitmap", new BitmapHandler(ahat)); 169 server.createContext("/style.css", new StaticHandler("style.css", "text/css")); 170 server.setExecutor(Executors.newFixedThreadPool(1)); 171 System.out.println("Server started on localhost:" + port); 172 173 server.start(); 174 } 175 } 176 177