Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | |
| 2 // for details. All rights reserved. Use of this source code is governed by a | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 import 'dart:collection'; | |
| 6 | |
| 7 import '../log/log.dart'; | |
| 8 import 'page_writer.dart'; | |
| 9 | |
| 10 /** | |
| 11 * A page writer that will produce the page containing statistics about an | |
| 12 * instrumentation log. | |
| 13 */ | |
| 14 class StatsPage extends PageWriter { | |
| 15 /** | |
| 16 * The instrumentation log to be written. | |
| 17 */ | |
| 18 final InstrumentationLog log; | |
| 19 | |
| 20 /** | |
| 21 * A table mapping the kinds of entries in the log to the number of each kind. | |
| 22 */ | |
| 23 final Map<String, int> entryCounts = new HashMap<String, int>(); | |
| 24 | |
| 25 /** | |
| 26 * The number of responses that returned an error. | |
| 27 */ | |
| 28 int errorCount = 0; | |
| 29 | |
| 30 /** | |
| 31 * A table mapping request method names to a list of the latencies associated | |
| 32 * with those requests, where the latency is defined to be the time between | |
| 33 * when the request was sent by the client and when the server started | |
| 34 * processing the request. | |
| 35 */ | |
| 36 final Map<String, List<int>> latencyData = new HashMap<String, List<int>>(); | |
| 37 | |
| 38 /** | |
| 39 * A list of the number of milliseconds between a completion request and the | |
| 40 * first event for that request. | |
| 41 */ | |
| 42 final List<int> completionResponseTimes = <int>[]; | |
| 43 | |
| 44 /** | |
| 45 * Initialize a newly created page writer to write information about the given | |
| 46 * instrumentation [log]. | |
| 47 */ | |
| 48 StatsPage(this.log) { | |
| 49 _processEntries(log.logEntries); | |
| 50 } | |
| 51 | |
| 52 @override | |
| 53 void writeBody(StringSink sink) { | |
| 54 writeMenu(sink); | |
| 55 sink.writeln('<div id="container">'); | |
| 56 sink.writeln(' <div id="content">'); | |
| 57 sink.writeln(' <div id="leftColumn">'); | |
| 58 sink.writeln(' <div class="inset">'); | |
| 59 _writeLeftColumn(sink); | |
| 60 sink.writeln(' </div>'); | |
| 61 sink.writeln(' </div>'); | |
| 62 sink.writeln(' <div id="rightColumn">'); | |
| 63 sink.writeln(' <div class="inset">'); | |
| 64 _writeRightColumn(sink); | |
| 65 sink.writeln(' </div>'); | |
| 66 sink.writeln(' </div>'); | |
| 67 sink.writeln(' </div>'); | |
| 68 sink.writeln('</div>'); | |
| 69 } | |
| 70 | |
| 71 /** | |
| 72 * Write the content of the style sheet (without the 'script' tag) for the | |
| 73 * page to the given [sink]. | |
| 74 */ | |
| 75 void writeStyleSheet(StringSink sink) { | |
| 76 super.writeStyleSheet(sink); | |
| 77 sink.writeln('#leftColumn {'); | |
| 78 sink.writeln(' float: left;'); | |
| 79 sink.writeln(' height: 100%;'); | |
| 80 sink.writeln(' overflow: auto;'); | |
| 81 sink.writeln(' width: 50%;'); | |
| 82 sink.writeln('}'); | |
| 83 sink.writeln('#rightColumn {'); | |
| 84 sink.writeln(' float: right;'); | |
| 85 sink.writeln(' height: 100%;'); | |
| 86 sink.writeln(' overflow: auto;'); | |
| 87 sink.writeln(' width: 50%;'); | |
| 88 sink.writeln('}'); | |
| 89 } | |
| 90 | |
| 91 /** | |
| 92 * Return the mean of the values in the given list of [latencies]. | |
| 93 */ | |
| 94 int _mean(List<int> latencies) { | |
|
scheglov
2016/09/14 16:17:17
The parameter could have a more generic name.
Brian Wilkerson
2016/09/14 16:39:27
Done
| |
| 95 int sum = latencies.fold(0, (int sum, int latency) => sum + latency); | |
| 96 return sum ~/ latencies.length; | |
| 97 } | |
| 98 | |
| 99 /** | |
| 100 * Return a table mapping the kinds of the given [entries] to the number of | |
| 101 * each kind. | |
| 102 */ | |
| 103 void _processEntries(List<LogEntry> entries) { | |
| 104 void increment/*<K>*/(Map<dynamic/*=K*/, int> map, dynamic/*=K*/ key) { | |
| 105 map[key] = (map[key] ?? 0) + 1; | |
| 106 } | |
| 107 | |
| 108 for (LogEntry entry in entries) { | |
| 109 String kind = entry.kind; | |
| 110 increment(entryCounts, kind); | |
| 111 if (entry is ResponseEntry) { | |
| 112 if (entry.result('error') != null) { | |
| 113 errorCount++; | |
| 114 } | |
| 115 } | |
| 116 | |
| 117 if (entry is RequestEntry) { | |
| 118 String method = entry.method; | |
| 119 int latency = entry.timeStamp - entry.clientRequestTime; | |
| 120 latencyData.putIfAbsent(method, () => new List<int>()).add(latency); | |
| 121 if (method == 'completion.getSuggestions') { | |
| 122 ResponseEntry response = log.responseFor(entry); | |
| 123 if (response != null) { | |
| 124 String id = response.result('id'); | |
| 125 if (id != null) { | |
| 126 List<NotificationEntry> events = log.completionEventsWithId(id); | |
| 127 if (events != null && events.length > 0) { | |
| 128 completionResponseTimes | |
| 129 .add(events[0].timeStamp - entry.timeStamp); | |
| 130 } | |
| 131 } | |
| 132 } | |
| 133 } | |
| 134 } | |
| 135 } | |
| 136 } | |
| 137 | |
| 138 void _writeLeftColumn(StringSink sink) { | |
| 139 List<String> filePaths = log.logFilePaths; | |
| 140 List<LogEntry> entries = log.logEntries; | |
| 141 DateTime startDate = entries[0].toTime; | |
| 142 DateTime endDate = entries[entries.length - 1].toTime; | |
| 143 Duration duration = endDate.difference(startDate); | |
| 144 List<String> entryKinds = entryCounts.keys.toList()..sort(); | |
| 145 | |
| 146 sink.writeln('<h3>General</h3>'); | |
| 147 sink.writeln('<p>'); | |
| 148 if (filePaths.length == 1) { | |
| 149 sink.write('<span class="label">Log file:</span> '); | |
| 150 sink.write(filePaths[0]); | |
| 151 } else { | |
| 152 sink.write('<span class="label">Log files:</span> '); | |
| 153 bool needsSeparator = false; | |
| 154 for (String path in filePaths) { | |
| 155 if (needsSeparator) { | |
| 156 sink.write(', '); | |
| 157 } else { | |
| 158 needsSeparator = true; | |
| 159 } | |
| 160 sink.write(path); | |
| 161 } | |
| 162 } | |
| 163 sink.writeln('<br>'); | |
| 164 sink.write('<span class="label">Start time:</span> '); | |
| 165 writeDate(sink, startDate); | |
| 166 sink.writeln('<br>'); | |
| 167 sink.write('<span class="label">End time:</span> '); | |
| 168 writeDate(sink, endDate); | |
| 169 sink.writeln('<br>'); | |
| 170 sink.write('<span class="label">Duration:</span> '); | |
| 171 sink.write(duration.toString()); | |
| 172 sink.writeln('</p>'); | |
| 173 | |
| 174 sink.writeln('<h3>Entries</h3>'); | |
| 175 sink.write('<p>'); | |
| 176 sink.write('<span class="label">Number of entries:</span> '); | |
| 177 sink.write(entries.length); | |
| 178 sink.writeln('</p>'); | |
| 179 sink.writeln('<table>'); | |
| 180 sink.writeln('<tr><th>count</th><th>kind</th></tr>'); | |
| 181 for (String kind in entryKinds) { | |
| 182 sink.write('<tr><td class="int">'); | |
| 183 sink.write(entryCounts[kind]); | |
| 184 sink.write('</td><td>'); | |
| 185 sink.write(kind); | |
| 186 sink.writeln('</td></tr>'); | |
| 187 } | |
| 188 sink.write('<tr><td class="int">'); | |
| 189 sink.write(entries.length); | |
| 190 sink.writeln('</td><td>Total</td></tr>'); | |
| 191 sink.writeln('</table>'); | |
| 192 } | |
| 193 | |
| 194 void _writeRightColumn(StringSink sink) { | |
| 195 List<String> methodNames = latencyData.keys.toList()..sort(); | |
| 196 completionResponseTimes.sort(); | |
| 197 | |
| 198 sink.writeln('<h3>Latency</h3>'); | |
| 199 sink.write('<p>'); | |
| 200 sink.write('<span class="label">Latency by method</span>'); | |
| 201 sink.writeln('</p>'); | |
| 202 sink.writeln('<table>'); | |
| 203 sink.writeln( | |
| 204 '<tr><th>min</th><th>mean</th><th>max</th><th>method</th></tr>'); | |
| 205 for (String method in methodNames) { | |
| 206 List<int> latencies = latencyData[method]..sort(); | |
| 207 // TODO(brianwilkerson) Add a spark-line distribution graph. | |
| 208 sink.write('<tr><td class="int">'); | |
| 209 sink.write(latencies[0]); | |
| 210 sink.write('</td><td class="int">'); | |
| 211 sink.write(_mean(latencies)); | |
| 212 sink.write('</td><td class="int">'); | |
| 213 sink.write(latencies[latencies.length - 1]); | |
| 214 sink.write('</td><td>'); | |
| 215 sink.write(method); | |
| 216 sink.writeln('</td></tr>'); | |
| 217 } | |
| 218 sink.writeln('</table>'); | |
| 219 | |
| 220 sink.writeln('<h3>Completion</h3>'); | |
| 221 sink.write('<p>'); | |
| 222 sink.write('<span class="label">Time to first notification:</span> '); | |
| 223 sink.write(completionResponseTimes[0]); | |
| 224 sink.write(', '); | |
| 225 sink.write(_mean(completionResponseTimes)); | |
| 226 sink.write(', '); | |
| 227 sink.write(completionResponseTimes[completionResponseTimes.length - 1]); | |
| 228 sink.writeln('</p>'); | |
| 229 } | |
| 230 } | |
| OLD | NEW |