/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ide.eclipse.ddms.systrace; import com.google.common.base.Charsets; import com.google.common.io.Files; import java.io.File; import java.io.IOException; import java.util.zip.DataFormatException; import java.util.zip.Inflater; /** {@link SystraceOutputParser} receives the output of atrace command run on the device, * parses it and generates html based on the trace */ public class SystraceOutputParser { private static final String TRACE_START = "TRACE:\n"; //$NON-NLS-1$ private static final String HTML_PREFIX = "<!DOCTYPE HTML>\n" + "<html>\n" + "<head i18n-values=\"dir:textdirection;\">\n" + "<title>Android System Trace</title>\n" + "%s\n" + "%s\n" + "<script language=\"javascript\">\n" + "document.addEventListener('DOMContentLoaded', function() {\n" + " if (!linuxPerfData)\n" + " return;\n" + " var m = new tracing.TimelineModel(linuxPerfData);\n" + " var timelineViewEl = document.querySelector('.view');\n" + " base.ui.decorate(timelineViewEl, tracing.TimelineView);\n" + " timelineViewEl.model = m;\n" + " timelineViewEl.tabIndex = 1;\n" + " timelineViewEl.timeline.focusElement = timelineViewEl;\n" + "});\n" + "</script>\n" + "<style>\n" + " .view {\n" + " overflow: hidden;\n" + " position: absolute;\n" + " top: 0;\n" + " bottom: 0;\n" + " left: 0;\n" + " right: 0;\n" + " }\n" + "</style>\n" + "</head>\n" + "<body>\n" + " <div class=\"view\">\n" + " </div>\n" + " <script>\n" + " var linuxPerfData = \"\\\n"; private static final String HTML_SUFFIX = " dummy-0000 [000] 0.0: 0: trace_event_clock_sync: parent_ts=0.0\\n\";\n" + " </script>\n" + "</body>\n" + "</html>\n"; private final boolean mUncompress; private final String mJs; private final String mCss; private byte[] mAtraceOutput; private int mAtraceLength; private int mSystraceIndex = -1; /** * Constructs a atrace output parser. * @param compressedStream Is the input stream compressed using zlib? * @param systraceJs systrace javascript content * @param systraceCss systrace css content */ public SystraceOutputParser(boolean compressedStream, String systraceJs, String systraceCss) { mUncompress = compressedStream; mJs = systraceJs; mCss = systraceCss; } /** * Parses the atrace output for systrace content. * @param atraceOutput output bytes from atrace */ public void parse(byte[] atraceOutput) { mAtraceOutput = atraceOutput; mAtraceLength = atraceOutput.length; removeCrLf(); // locate the trace start marker within the first hundred bytes String header = new String(mAtraceOutput, 0, Math.min(100, mAtraceLength)); mSystraceIndex = locateSystraceData(header); if (mSystraceIndex < 0) { throw new RuntimeException("Unable to find trace start marker 'TRACE:':\n" + header); } } /** Replaces \r\n with \n in {@link #mAtraceOutput}. */ private void removeCrLf() { int dst = 0; for (int src = 0; src < mAtraceLength - 1; src++, dst++) { byte copy; if (mAtraceOutput[src] == '\r' && mAtraceOutput[src + 1] == '\n') { copy = '\n'; src++; } else { copy = mAtraceOutput[src]; } mAtraceOutput[dst] = copy; } mAtraceLength = dst; } private int locateSystraceData(String header) { int index = header.indexOf(TRACE_START); if (index < 0) { return -1; } else { return index + TRACE_START.length(); } } public String getSystraceHtml() { if (mSystraceIndex < 0) { return ""; } String trace = ""; if (mUncompress) { Inflater decompressor = new Inflater(); decompressor.setInput(mAtraceOutput, mSystraceIndex, mAtraceLength - mSystraceIndex); byte[] buf = new byte[4096]; int n; StringBuilder sb = new StringBuilder(1000); try { while ((n = decompressor.inflate(buf)) > 0) { sb.append(new String(buf, 0, n)); } } catch (DataFormatException e) { throw new RuntimeException(e); } decompressor.end(); trace = sb.toString(); } else { trace = new String(mAtraceOutput, mSystraceIndex, mAtraceLength - mSystraceIndex); } // each line should end with the characters \n\ followed by a newline String html_out = trace.replaceAll("\n", "\\\\n\\\\\n"); String header = String.format(HTML_PREFIX, mCss, mJs); String footer = HTML_SUFFIX; return header + html_out + footer; } public static String getJs(File assetsFolder) { try { return String.format("<script language=\"javascript\">%s</script>", Files.toString(new File(assetsFolder, "script.js"), Charsets.UTF_8)); } catch (IOException e) { return ""; } } public static String getCss(File assetsFolder) { try { return String.format("<style type=\"text/css\">%s</style>", Files.toString(new File(assetsFolder, "style.css"), Charsets.UTF_8)); } catch (IOException e) { return ""; } } }