Home | History | Annotate | Download | only in systrace_analysis
      1 <!DOCTYPE html>
      2 <!--
      3 Copyright (C) 2015 The Android Open Source Project
      4 
      5 Licensed under the Apache License, Version 2.0 (the "License");
      6 you may not use this file except in compliance with the License.
      7 You may obtain a copy of the License at
      8 
      9      http://www.apache.org/licenses/LICENSE-2.0
     10 
     11 Unless required by applicable law or agreed to in writing, software
     12 distributed under the License is distributed on an "AS IS" BASIS,
     13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 See the License for the specific language governing permissions and
     15 limitations under the License.
     16 -->
     17 <link rel="import" href="/tracing/base/base.html">
     18 <link rel="import" href="/tracing/extras/importer/linux_perf/ftrace_importer.html">
     19 <link rel="import" href="/tracing/extras/importer/android/event_log_importer.html">
     20 <link rel="import" href="/tracing/extras/android/android_auditor.html">
     21 <link rel="import" href="/tracing/importer/import.html">
     22 <link rel="import" href="/tracing/model/model.html">
     23 <script>
     24 'use strict';
     25 
     26 (function() {
     27   var FRAME_PERF_CLASS = tr.model.FRAME_PERF_CLASS;
     28 
     29   if (!tr.isHeadless) {
     30     throw new Error('Can only run in headless mode.');
     31   }
     32 
     33   function printFrameStats(process, range) {
     34     var goodFrames = 0;
     35     var badFrames = 0;
     36     var neutralFrames = 0;
     37     var terribleFrames = 0;
     38 
     39     process.frames.forEach(function(frame) {
     40       // Check if frame belongs to any activity
     41       if (frame.start >= range.min && (frame.end <= range.max)) {
     42         if (frame.perfClass == FRAME_PERF_CLASS.NEUTRAL) neutralFrames++;
     43         if (frame.perfClass == FRAME_PERF_CLASS.GOOD) goodFrames++;
     44         if (frame.perfClass == FRAME_PERF_CLASS.BAD) badFrames++;
     45         if (frame.perfClass == FRAME_PERF_CLASS.TERRIBLE) terribleFrames++;
     46       }
     47     });
     48 
     49     var totalFrames = goodFrames + badFrames + neutralFrames;
     50     if (totalFrames > 0) {
     51       console.log("  Frame stats:");
     52       console.log("    # Total frames: " + totalFrames);
     53       console.log("    # Terrible frames: " + terribleFrames);
     54       console.log("    # Bad frames: " + badFrames);
     55       console.log("    # Neutral frames: " + neutralFrames);
     56       console.log("    # Good frames: " + goodFrames);
     57     }
     58   };
     59 
     60   function printMemoryStats(process, range) {
     61     var numDirectReclaim = 0;
     62     for (var tid in process.threads) {
     63       if (!process.threads[tid].timeSlices) continue;
     64       process.threads[tid].sliceGroup.slices.forEach(function(slice) {
     65         if (slice.title.startsWith('direct reclaim')) {
     66           if (slice.start >= range.min &&
     67               slice.start + slice.duration <= range.max) {
     68             numDirectReclaim++;
     69           }
     70         }
     71       });
     72     }
     73     console.log("  Memory stats:");
     74     console.log("    # of direct reclaims: " + numDirectReclaim);
     75   };
     76 
     77   function printCpuStatsForThread(thread, range) {
     78     var stats = thread.getCpuStatsForRange(range);
     79     // Sum total CPU duration
     80     console.log('    ' + thread.name + ' CPU allocation: ');
     81     for (var cpu in stats) {
     82       var percentage = (stats[cpu] / stats.total * 100).toFixed(2);
     83 
     84       console.log("      CPU " + cpu + ": " + percentage + "% (" +
     85           stats[cpu].toFixed(2) + " ms.)");
     86     }
     87   };
     88 
     89   function printCpuStatsForProcess(process, range) {
     90     var total_runtime = 0;
     91     for (var tid in process.threads) {
     92       var stats = process.threads[tid].getCpuStatsForRange(range);
     93       total_runtime += stats.total;
     94     }
     95     console.log("  CPU stats:");
     96     console.log("    Total CPU runtime: " + total_runtime.toFixed(2) + " ms.");
     97     var uiThread = process.getThread(process.pid);
     98     var renderThreads = process.findAllThreadsNamed('RenderThread');
     99     var renderThread = renderThreads.length == 1 ? renderThreads[0] : undefined;
    100     if (uiThread)
    101       printCpuStatsForThread(uiThread, range);
    102     if (renderThread)
    103       printCpuStatsForThread(renderThread, range);
    104     printCpuFreqStats(range);
    105   };
    106 
    107   function printCpuFreqStats(range) {
    108     for (var i = 0; i < 8; i++) {
    109       var cpu = model.kernel.getOrCreateCpu(i);
    110       if (cpu !== undefined) {
    111         var stats = cpu.getFreqStatsForRange(range);
    112 
    113         console.log('    CPU ' + i + ' frequency distribution:');
    114         for (var freq in stats) {
    115           var percentage = (stats[freq] / range.duration * 100).toFixed(2);
    116           console.log('      ' + freq + ' ' + percentage + "% (" +
    117               stats[freq].toFixed(2)  + ' ms.)');
    118         }
    119       }
    120     }
    121   };
    122 
    123   function printBinderStats(process, range) {
    124     var outgoing_transactions = 0;
    125     var incoming_transactions = 0;
    126     for (var tid in process.threads) {
    127       var outgoing_slices = process.threads[tid].sliceGroup.getSlicesOfName('binder transaction');
    128       var outgoing_async_slices = process.threads[tid].sliceGroup.getSlicesOfName('binder transaction async');
    129       var incoming_slices = process.threads[tid].sliceGroup.getSlicesOfName('binder reply');
    130       var incoming_async_slices = process.threads[tid].sliceGroup.getSlicesOfName('binder Async recv');
    131       outgoing_transactions += outgoing_slices.length + outgoing_async_slices.length;
    132       incoming_transactions += incoming_slices.length + incoming_async_slices.length;
    133     }
    134     console.log('  Binder transaction stats:');
    135     console.log('    # Outgoing binder transactions: ' + outgoing_transactions);
    136     console.log('    # Incoming binder transactions: ' + incoming_transactions);
    137   };
    138 
    139   function pagesInMBString(pages) {
    140     if (isNaN(pages))
    141       return '0 (0.00 MB)';
    142     return pages + ' (' + (pages * 4096 / 1024 / 1024).toFixed(2) + ' MB)';
    143   };
    144 
    145   function printPageCacheStats(process) {
    146     console.log('  Page cache stats:');
    147     var totalAccess = 0;
    148     var totalMiss = 0;
    149     var totalAdd = 0;
    150     for (var file in process.pageCacheAccesses) {
    151       totalAccess += process.pageCacheAccesses[file];
    152       totalMiss += process.pageCacheMisses[file];
    153       totalAdd += process.pageCacheAdd[file];
    154       console.log('    File: ' + file);
    155       console.log('        # of pages accessed: ' + pagesInMBString(process.pageCacheAccesses[file]));
    156       console.log('        # of pages missed: ' + pagesInMBString(process.pageCacheMisses[file]));
    157       console.log('        # of pages added to cache: ' + pagesInMBString(process.pageCacheAdd[file]));
    158     }
    159     console.log('    TOTALS:');
    160     console.log('        # of pages accessed: ' + pagesInMBString(totalAccess));
    161     console.log('        # of pages missed: ' + pagesInMBString(totalMiss));
    162     console.log('        # of pages added to cache: ' + pagesInMBString(totalAdd));
    163   };
    164 
    165   function printProcessStats(process, opt_range) {
    166     var range = opt_range;
    167     if (range === undefined) {
    168       // Use the process range
    169       range = process.bounds;
    170     }
    171     printCpuStatsForProcess(process, range);
    172     printPageCacheStats(process);
    173     printMemoryStats(process, range);
    174     printBinderStats(process, range);
    175     printFrameStats(process, range);
    176   };
    177 
    178   if (sys.argv.length < 2)
    179     console.log('First argument needs to be a systrace file.');
    180 
    181   // Import model
    182   var systrace = read(sys.argv[1]);
    183   var traces = [systrace];
    184   if (sys.argv.length >= 3) {
    185     // Add event log file if we got it
    186     var events = read(sys.argv[2]);
    187     traces.push(events);
    188   }
    189   var model = new tr.Model();
    190   var i = new tr.importer.Import(model);
    191   console.log("Starting import...");
    192   i.importTraces(traces);
    193   console.log("Done.");
    194   model.getAllProcesses().forEach(function(process) {
    195     if (process.name === undefined) return;
    196     console.log('Stats for process ' + process.name + ' (' + process.pid + ')');
    197     // Check if process has activity starts
    198     if (process.activities && process.activities.length > 0) {
    199       process.activities.forEach(function(activity) {
    200         console.log('Activity ' + activity.name + ' foreground from ' +
    201             activity.start + ' until ' + activity.end);
    202         var activityRange = tr.b.Range.fromExplicitRange(activity.start,
    203             activity.start + activity.duration);
    204         printProcessStats(process, activityRange);
    205       }, this);
    206     } else {
    207       printProcessStats(process);
    208     }
    209     console.log('');
    210   });
    211 })();
    212 </script>
    213