Home | History | Annotate | Download | only in ahat
      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