1<!DOCTYPE html>
2<!--
3Copyright (C) 2015 The Android Open Source Project
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9     http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations 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