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
17package dalvik.system.profiler;
18
19import java.io.BufferedInputStream;
20import java.io.Closeable;
21import java.io.DataInputStream;
22import java.io.EOFException;
23import java.io.File;
24import java.io.FileInputStream;
25import java.io.IOException;
26import 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 */
35public final class HprofBinaryToAscii {
36
37    /**
38     * Main entry point for HprofBinaryToAscii command line tool
39     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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