Home | History | Annotate | Download | only in systrace
      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