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