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 static final String HTML_PREFIX = "<!DOCTYPE HTML>\n" 33 + "<html>\n" 34 + "<head i18n-values=\"dir:textdirection;\">\n" 35 + "<title>Android System Trace</title>\n" 36 + "%s\n" 37 + "%s\n" 38 + "<script language=\"javascript\">\n" 39 + "document.addEventListener('DOMContentLoaded', function() {\n" 40 + " if (!linuxPerfData)\n" 41 + " return;\n" 42 + " var m = new tracing.TimelineModel(linuxPerfData);\n" 43 + " var timelineViewEl = document.querySelector('.view');\n" 44 + " base.ui.decorate(timelineViewEl, tracing.TimelineView);\n" 45 + " timelineViewEl.model = m;\n" 46 + " timelineViewEl.tabIndex = 1;\n" 47 + " timelineViewEl.timeline.focusElement = timelineViewEl;\n" 48 + "});\n" 49 + "</script>\n" 50 + "<style>\n" 51 + " .view {\n" 52 + " overflow: hidden;\n" 53 + " position: absolute;\n" 54 + " top: 0;\n" 55 + " bottom: 0;\n" 56 + " left: 0;\n" 57 + " right: 0;\n" 58 + " }\n" 59 + "</style>\n" 60 + "</head>\n" 61 + "<body>\n" 62 + " <div class=\"view\">\n" 63 + " </div>\n" 64 + " <script>\n" 65 + " var linuxPerfData = \"\\\n"; 66 67 private static final String HTML_SUFFIX = 68 " dummy-0000 [000] 0.0: 0: trace_event_clock_sync: parent_ts=0.0\\n\";\n" 69 + " </script>\n" 70 + "</body>\n" 71 + "</html>\n"; 72 73 private final boolean mUncompress; 74 private final String mJs; 75 private final String mCss; 76 77 private byte[] mAtraceOutput; 78 private int mAtraceLength; 79 private int mSystraceIndex = -1; 80 81 /** 82 * Constructs a atrace output parser. 83 * @param compressedStream Is the input stream compressed using zlib? 84 * @param systraceJs systrace javascript content 85 * @param systraceCss systrace css content 86 */ 87 public SystraceOutputParser(boolean compressedStream, String systraceJs, String systraceCss) { 88 mUncompress = compressedStream; 89 mJs = systraceJs; 90 mCss = systraceCss; 91 } 92 93 /** 94 * Parses the atrace output for systrace content. 95 * @param atraceOutput output bytes from atrace 96 */ 97 public void parse(byte[] atraceOutput) { 98 mAtraceOutput = atraceOutput; 99 mAtraceLength = atraceOutput.length; 100 101 removeCrLf(); 102 103 // locate the trace start marker within the first hundred bytes 104 String header = new String(mAtraceOutput, 0, Math.min(100, mAtraceLength)); 105 mSystraceIndex = locateSystraceData(header); 106 107 if (mSystraceIndex < 0) { 108 throw new RuntimeException("Unable to find trace start marker 'TRACE:':\n" + header); 109 } 110 } 111 112 /** Replaces \r\n with \n in {@link #mAtraceOutput}. */ 113 private void removeCrLf() { 114 int dst = 0; 115 for (int src = 0; src < mAtraceLength - 1; src++, dst++) { 116 byte copy; 117 if (mAtraceOutput[src] == '\r' && mAtraceOutput[src + 1] == '\n') { 118 copy = '\n'; 119 src++; 120 } else { 121 copy = mAtraceOutput[src]; 122 } 123 mAtraceOutput[dst] = copy; 124 } 125 126 mAtraceLength = dst; 127 } 128 129 private int locateSystraceData(String header) { 130 int index = header.indexOf(TRACE_START); 131 if (index < 0) { 132 return -1; 133 } else { 134 return index + TRACE_START.length(); 135 } 136 } 137 138 public String getSystraceHtml() { 139 if (mSystraceIndex < 0) { 140 return ""; 141 } 142 143 String trace = ""; 144 if (mUncompress) { 145 Inflater decompressor = new Inflater(); 146 decompressor.setInput(mAtraceOutput, mSystraceIndex, mAtraceLength - mSystraceIndex); 147 148 byte[] buf = new byte[4096]; 149 int n; 150 StringBuilder sb = new StringBuilder(1000); 151 try { 152 while ((n = decompressor.inflate(buf)) > 0) { 153 sb.append(new String(buf, 0, n)); 154 } 155 } catch (DataFormatException e) { 156 throw new RuntimeException(e); 157 } 158 decompressor.end(); 159 160 trace = sb.toString(); 161 } else { 162 trace = new String(mAtraceOutput, mSystraceIndex, mAtraceLength - mSystraceIndex); 163 } 164 165 // each line should end with the characters \n\ followed by a newline 166 String html_out = trace.replaceAll("\n", "\\\\n\\\\\n"); 167 String header = String.format(HTML_PREFIX, mCss, mJs); 168 String footer = HTML_SUFFIX; 169 return header + html_out + footer; 170 } 171 172 public static String getJs(File assetsFolder) { 173 try { 174 return String.format("<script language=\"javascript\">%s</script>", 175 Files.toString(new File(assetsFolder, "script.js"), Charsets.UTF_8)); 176 } catch (IOException e) { 177 return ""; 178 } 179 } 180 181 public static String getCss(File assetsFolder) { 182 try { 183 return String.format("<style type=\"text/css\">%s</style>", 184 Files.toString(new File(assetsFolder, "style.css"), Charsets.UTF_8)); 185 } catch (IOException e) { 186 return ""; 187 } 188 } 189 } 190