1 /*
2  * Copyright (C) 2010 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 dalvik.system.profiler;
18 
19 import java.io.BufferedInputStream;
20 import java.io.Closeable;
21 import java.io.DataInputStream;
22 import java.io.EOFException;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 
28 /**
29  * Run on device with:
30  * adb shell dalvikvm 'dalvik.system.profiler.HprofBinaryToAscii'
31  *
32  * Run on host with:
33  * java -classpath out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar
34  */
35 public final class HprofBinaryToAscii {
36 
37     /**
38      * Main entry point for HprofBinaryToAscii command line tool
39      */
main(String[] args)40     public static void main(String[] args) {
41         System.exit(convert(args) ? 0 : 1);
42     }
43 
44     /**
45      * Reads single file from arguments and attempts to read it as
46      * either a binary hprof file or a version with a text header.
47      */
convert(String[] args)48     private static boolean convert(String[] args) {
49 
50         if (args.length != 1) {
51             usage("binary hprof file argument expected");
52             return false;
53         }
54         File file = new File(args[0]);
55         if (!file.exists()) {
56             usage("file " + file + " does not exist");
57             return false;
58         }
59 
60         if (startsWithMagic(file)) {
61             HprofData hprofData;
62             try {
63                 hprofData = readHprof(file);
64             } catch (IOException e) {
65                 System.out.println("Problem reading binary hprof data from "
66                                    + file + ": " + e.getMessage());
67                 return false;
68             }
69             return write(hprofData);
70         }
71 
72         HprofData hprofData;
73         try {
74             hprofData = readSnapshot(file);
75         } catch (IOException e) {
76             System.out.println("Problem reading snapshot containing binary hprof data from "
77                                + file + ": " + e.getMessage());
78             return false;
79         }
80         return write(hprofData);
81     }
82 
83     /**
84      * Probe the start of file to see if it starts with a plausible
85      * binary hprof magic value. If so, it is returned. On any other
86      * case including unexpected errors, false is returned.
87      */
startsWithMagic(File file)88     private static boolean startsWithMagic(File file) {
89         DataInputStream inputStream = null;
90         try {
91             inputStream = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
92             return BinaryHprof.readMagic(inputStream) != null;
93         } catch (IOException e) {
94             return false;
95         } finally {
96             closeQuietly(inputStream);
97         }
98     }
99 
100     /**
101      * Read and return an HprofData from a vanilla binary hprof file.
102      */
readHprof(File file)103     private static HprofData readHprof(File file) throws IOException {
104         InputStream inputStream = null;
105         try {
106             inputStream = new BufferedInputStream(new FileInputStream(file));
107             return read(inputStream);
108         } finally {
109             closeQuietly(inputStream);
110         }
111     }
112 
113     /**
114      * Read a file looking for text header terminated by two newlines,
115      * then proceed to read binary hprof data.
116      */
readSnapshot(File file)117     private static HprofData readSnapshot(File file) throws IOException {
118         InputStream inputStream = null;
119         try {
120             inputStream = new BufferedInputStream(new FileInputStream(file));
121             int ch;
122             while ((ch = inputStream.read()) != -1) {
123                 if (ch == '\n' && inputStream.read() == '\n') {
124                     return read(inputStream);
125                 }
126             }
127             throw new EOFException("Could not find expected header");
128         } finally {
129             closeQuietly(inputStream);
130         }
131     }
132 
133     /**
134      * Read binary hprof data from the provided input stream and
135      * return the HprofData object.
136      */
read(InputStream inputStream)137     private static HprofData read(InputStream inputStream) throws IOException {
138         BinaryHprofReader reader = new BinaryHprofReader(inputStream);
139         reader.setStrict(false);
140         reader.read();
141         return reader.getHprofData();
142     }
143 
144     /**
145      * From IoUtils.closeQuietly but replicated for open source
146      * version.
147      */
closeQuietly(Closeable c)148     private static void closeQuietly(Closeable c) {
149         if (c != null) {
150             try {
151                 c.close();
152             } catch (IOException ignored) {
153             }
154         }
155     }
156 
157     /**
158      * Write text verion of hprof data to standard output. Returns
159      * false on error.
160      */
write(HprofData hprofData)161     private static boolean write(HprofData hprofData) {
162         try {
163             AsciiHprofWriter.write(hprofData, System.out);
164         } catch (IOException e) {
165             System.out.println("Problem writing ASCII hprof data: " + e.getMessage());
166             return false;
167         }
168         return true;
169     }
170 
171     /**
172      * Prints usage error but does not exit.
173      */
usage(String error)174     private static void usage(String error) {
175         System.out.print("ERROR: ");
176         System.out.println(error);
177         System.out.println();
178         System.out.println("usage: HprofBinaryToAscii <binary-hprof-file>");
179         System.out.println();
180         System.out.println("Reads a binary hprof file and print it in ASCII format");
181     }
182 }
183