1 /*
2  * Copyright (C) 2012 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.ide.eclipse.ddms.systrace;
18 
19 import com.google.common.base.Charsets;
20 import com.google.common.io.Files;
21 
22 import java.io.File;
23 import java.io.IOException;
24 import java.util.zip.DataFormatException;
25 import java.util.zip.Inflater;
26 
27 /** {@link SystraceOutputParser} receives the output of atrace command run on the device,
28  * parses it and generates html based on the trace */
29 public class SystraceOutputParser {
30     private static final String TRACE_START = "TRACE:\n"; //$NON-NLS-1$
31 
32     private final boolean mUncompress;
33     private final String mJs;
34     private final String mCss;
35     private final String mHtmlPrefix;
36     private final String mHtmlSuffix;
37 
38     private byte[] mAtraceOutput;
39     private int mAtraceLength;
40     private int mSystraceIndex = -1;
41 
42     /**
43      * Constructs a atrace output parser.
44      * @param compressedStream Is the input stream compressed using zlib?
45      * @param systraceJs systrace javascript content
46      * @param systraceCss systrace css content
47      */
SystraceOutputParser(boolean compressedStream, String systraceJs, String systraceCss, String htmlPrefix, String htmlSuffix)48     public SystraceOutputParser(boolean compressedStream, String systraceJs, String systraceCss,
49             String htmlPrefix, String htmlSuffix) {
50         mUncompress = compressedStream;
51         mJs = systraceJs;
52         mCss = systraceCss;
53         mHtmlPrefix = htmlPrefix;
54         mHtmlSuffix = htmlSuffix;
55     }
56 
57     /**
58      * Parses the atrace output for systrace content.
59      * @param atraceOutput output bytes from atrace
60      */
parse(byte[] atraceOutput)61     public void parse(byte[] atraceOutput) {
62         mAtraceOutput = atraceOutput;
63         mAtraceLength = atraceOutput.length;
64 
65         removeCrLf();
66 
67         // locate the trace start marker within the first hundred bytes
68         String header = new String(mAtraceOutput, 0, Math.min(100, mAtraceLength));
69         mSystraceIndex = locateSystraceData(header);
70 
71         if (mSystraceIndex < 0) {
72             throw new RuntimeException("Unable to find trace start marker 'TRACE:':\n" + header);
73         }
74     }
75 
76     /** Replaces \r\n with \n in {@link #mAtraceOutput}. */
removeCrLf()77     private void removeCrLf() {
78         int dst = 0;
79         for (int src = 0; src < mAtraceLength - 1; src++, dst++) {
80             byte copy;
81             if (mAtraceOutput[src] == '\r' && mAtraceOutput[src + 1] == '\n') {
82                 copy = '\n';
83                 src++;
84             } else {
85                 copy = mAtraceOutput[src];
86             }
87             mAtraceOutput[dst] = copy;
88         }
89 
90         mAtraceLength = dst;
91     }
92 
locateSystraceData(String header)93     private int locateSystraceData(String header) {
94         int index = header.indexOf(TRACE_START);
95         if (index < 0) {
96             return -1;
97         } else {
98             return index + TRACE_START.length();
99         }
100     }
101 
getSystraceHtml()102     public String getSystraceHtml() {
103         if (mSystraceIndex < 0) {
104             return "";
105         }
106 
107         String trace = "";
108         if (mUncompress) {
109             Inflater decompressor = new Inflater();
110             decompressor.setInput(mAtraceOutput, mSystraceIndex, mAtraceLength - mSystraceIndex);
111 
112             byte[] buf = new byte[4096];
113             int n;
114             StringBuilder sb = new StringBuilder(1000);
115             try {
116                 while ((n = decompressor.inflate(buf)) > 0) {
117                     sb.append(new String(buf, 0, n));
118                 }
119             } catch (DataFormatException e) {
120                 throw new RuntimeException(e);
121             }
122             decompressor.end();
123 
124             trace = sb.toString();
125         } else {
126             trace = new String(mAtraceOutput, mSystraceIndex, mAtraceLength - mSystraceIndex);
127         }
128 
129         // each line should end with the characters \n\ followed by a newline
130         String html_out = trace.replaceAll("\n", "\\\\n\\\\\n");
131         String header = String.format(mHtmlPrefix, mCss, mJs, "");
132         String footer = mHtmlSuffix;
133         return header + html_out + footer;
134     }
135 
getJs(File assetsFolder)136     public static String getJs(File assetsFolder) {
137         try {
138             return String.format("<script language=\"javascript\">%s</script>",
139                     Files.toString(new File(assetsFolder, "script.js"), Charsets.UTF_8));
140         } catch (IOException e) {
141             return "";
142         }
143     }
144 
getCss(File assetsFolder)145     public static String getCss(File assetsFolder) {
146         try {
147             return String.format("<style type=\"text/css\">%s</style>",
148                     Files.toString(new File(assetsFolder, "style.css"), Charsets.UTF_8));
149         } catch (IOException e) {
150             return "";
151         }
152     }
153 
getHtmlPrefix(File assetsFolder)154     public static String getHtmlPrefix(File assetsFolder) {
155         return getHtmlTemplate(assetsFolder, "prefix.html");
156     }
157 
getHtmlSuffix(File assetsFolder)158     public static String getHtmlSuffix(File assetsFolder) {
159         return getHtmlTemplate(assetsFolder, "suffix.html");
160     }
161 
getHtmlTemplate(File assetsFolder, String htmlFileName)162     private static String getHtmlTemplate(File assetsFolder, String htmlFileName) {
163         try {
164             return Files.toString(new File(assetsFolder, htmlFileName), Charsets.UTF_8);
165         } catch (IOException e) {
166             return "";
167         }
168     }
169 }
170