OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 var SourceEntry = (function() { |
| 6 'use strict'; |
| 7 |
| 8 /** |
| 9 * A SourceEntry gathers all log entries with the same source. |
| 10 * |
| 11 * @constructor |
| 12 */ |
| 13 function SourceEntry(logEntry, maxPreviousSourceId) { |
| 14 this.maxPreviousSourceId_ = maxPreviousSourceId; |
| 15 this.entries_ = []; |
| 16 this.description_ = ''; |
| 17 |
| 18 // Set to true on most net errors. |
| 19 this.isError_ = false; |
| 20 |
| 21 // If the first entry is a BEGIN_PHASE, set to false. |
| 22 // Set to true when an END_PHASE matching the first entry is encountered. |
| 23 this.isInactive_ = true; |
| 24 |
| 25 if (logEntry.phase == EventPhase.PHASE_BEGIN) |
| 26 this.isInactive_ = false; |
| 27 |
| 28 this.update(logEntry); |
| 29 } |
| 30 |
| 31 SourceEntry.prototype = { |
| 32 update: function(logEntry) { |
| 33 // Only the last event should have the same type first event, |
| 34 if (!this.isInactive_ && |
| 35 logEntry.phase == EventPhase.PHASE_END && |
| 36 logEntry.type == this.entries_[0].type) { |
| 37 this.isInactive_ = true; |
| 38 } |
| 39 |
| 40 // If we have a net error code, update |this.isError_| if appropriate. |
| 41 if (logEntry.params) { |
| 42 var netErrorCode = logEntry.params.net_error; |
| 43 // Skip both cases where netErrorCode is undefined, and cases where it |
| 44 // is 0, indicating no actual error occurred. |
| 45 if (netErrorCode) { |
| 46 // Ignore error code caused by not finding an entry in the cache. |
| 47 if (logEntry.type != EventType.HTTP_CACHE_OPEN_ENTRY || |
| 48 netErrorCode != NetError.ERR_FAILED) { |
| 49 this.isError_ = true; |
| 50 } |
| 51 } |
| 52 } |
| 53 |
| 54 var prevStartEntry = this.getStartEntry_(); |
| 55 this.entries_.push(logEntry); |
| 56 var curStartEntry = this.getStartEntry_(); |
| 57 |
| 58 // If we just got the first entry for this source. |
| 59 if (prevStartEntry != curStartEntry) |
| 60 this.updateDescription_(); |
| 61 }, |
| 62 |
| 63 updateDescription_: function() { |
| 64 var e = this.getStartEntry_(); |
| 65 this.description_ = ''; |
| 66 if (!e) |
| 67 return; |
| 68 |
| 69 if (e.source.type == EventSourceType.NONE) { |
| 70 // NONE is what we use for global events that aren't actually grouped |
| 71 // by a "source ID", so we will just stringize the event's type. |
| 72 this.description_ = EventTypeNames[e.type]; |
| 73 return; |
| 74 } |
| 75 |
| 76 if (e.params == undefined) { |
| 77 return; |
| 78 } |
| 79 |
| 80 switch (e.source.type) { |
| 81 case EventSourceType.URL_REQUEST: |
| 82 // TODO(ricea): Remove SOCKET_STREAM after M41 is released. |
| 83 case EventSourceType.SOCKET_STREAM: |
| 84 case EventSourceType.HTTP_STREAM_JOB: |
| 85 this.description_ = e.params.url; |
| 86 break; |
| 87 case EventSourceType.CONNECT_JOB: |
| 88 this.description_ = e.params.group_name; |
| 89 break; |
| 90 case EventSourceType.HOST_RESOLVER_IMPL_JOB: |
| 91 case EventSourceType.HOST_RESOLVER_IMPL_PROC_TASK: |
| 92 this.description_ = e.params.host; |
| 93 break; |
| 94 case EventSourceType.DISK_CACHE_ENTRY: |
| 95 case EventSourceType.MEMORY_CACHE_ENTRY: |
| 96 this.description_ = e.params.key; |
| 97 break; |
| 98 case EventSourceType.QUIC_SESSION: |
| 99 if (e.params.host != undefined) |
| 100 this.description_ = e.params.host; |
| 101 break; |
| 102 case EventSourceType.HTTP2_SESSION: |
| 103 if (e.params.host) |
| 104 this.description_ = e.params.host + ' (' + e.params.proxy + ')'; |
| 105 break; |
| 106 case EventSourceType.HTTP_PIPELINED_CONNECTION: |
| 107 if (e.params.host_and_port) |
| 108 this.description_ = e.params.host_and_port; |
| 109 break; |
| 110 case EventSourceType.SOCKET: |
| 111 case EventSourceType.PROXY_CLIENT_SOCKET: |
| 112 // Use description of parent source, if any. |
| 113 if (e.params.source_dependency != undefined) { |
| 114 var parentId = e.params.source_dependency.id; |
| 115 this.description_ = |
| 116 SourceTracker.getInstance().getDescription(parentId); |
| 117 } |
| 118 break; |
| 119 case EventSourceType.UDP_SOCKET: |
| 120 if (e.params.address != undefined) { |
| 121 this.description_ = e.params.address; |
| 122 // If the parent of |this| is a HOST_RESOLVER_IMPL_JOB, use |
| 123 // '<DNS Server IP> [<host we're resolving>]'. |
| 124 if (this.entries_[0].type == EventType.SOCKET_ALIVE && |
| 125 this.entries_[0].params && |
| 126 this.entries_[0].params.source_dependency != undefined) { |
| 127 var parentId = this.entries_[0].params.source_dependency.id; |
| 128 var parent = SourceTracker.getInstance().getSourceEntry(parentId); |
| 129 if (parent && |
| 130 parent.getSourceType() == |
| 131 EventSourceType.HOST_RESOLVER_IMPL_JOB && |
| 132 parent.getDescription().length > 0) { |
| 133 this.description_ += ' [' + parent.getDescription() + ']'; |
| 134 } |
| 135 } |
| 136 } |
| 137 break; |
| 138 case EventSourceType.ASYNC_HOST_RESOLVER_REQUEST: |
| 139 case EventSourceType.DNS_TRANSACTION: |
| 140 this.description_ = e.params.hostname; |
| 141 break; |
| 142 case EventSourceType.DOWNLOAD: |
| 143 switch (e.type) { |
| 144 case EventType.DOWNLOAD_FILE_RENAMED: |
| 145 this.description_ = e.params.new_filename; |
| 146 break; |
| 147 case EventType.DOWNLOAD_FILE_OPENED: |
| 148 this.description_ = e.params.file_name; |
| 149 break; |
| 150 case EventType.DOWNLOAD_ITEM_ACTIVE: |
| 151 this.description_ = e.params.file_name; |
| 152 break; |
| 153 } |
| 154 break; |
| 155 case EventSourceType.FILESTREAM: |
| 156 this.description_ = e.params.file_name; |
| 157 break; |
| 158 case EventSourceType.IPV6_PROBE_JOB: |
| 159 if (e.type == EventType.IPV6_PROBE_RUNNING && |
| 160 e.phase == EventPhase.PHASE_END) { |
| 161 this.description_ = e.params.ipv6_supported ? 'IPv6 Supported' : |
| 162 'IPv6 Not Supported'; |
| 163 } |
| 164 break; |
| 165 } |
| 166 |
| 167 if (this.description_ == undefined) |
| 168 this.description_ = ''; |
| 169 }, |
| 170 |
| 171 /** |
| 172 * Returns a description for this source log stream, which will be displayed |
| 173 * in the list view. Most often this is a URL that identifies the request, |
| 174 * or a hostname for a connect job, etc... |
| 175 */ |
| 176 getDescription: function() { |
| 177 return this.description_; |
| 178 }, |
| 179 |
| 180 /** |
| 181 * Returns the starting entry for this source. Conceptually this is the |
| 182 * first entry that was logged to this source. However, we skip over the |
| 183 * TYPE_REQUEST_ALIVE entries which wrap TYPE_URL_REQUEST_START_JOB |
| 184 * entries. |
| 185 */ |
| 186 getStartEntry_: function() { |
| 187 if (this.entries_.length < 1) |
| 188 return undefined; |
| 189 if (this.entries_[0].source.type == EventSourceType.FILESTREAM) { |
| 190 var e = this.findLogEntryByType_(EventType.FILE_STREAM_OPEN); |
| 191 if (e != undefined) |
| 192 return e; |
| 193 } |
| 194 if (this.entries_[0].source.type == EventSourceType.DOWNLOAD) { |
| 195 // If any rename occurred, use the last name |
| 196 e = this.findLastLogEntryStartByType_( |
| 197 EventType.DOWNLOAD_FILE_RENAMED); |
| 198 if (e != undefined) |
| 199 return e; |
| 200 // Otherwise, if the file was opened, use that name |
| 201 e = this.findLogEntryByType_(EventType.DOWNLOAD_FILE_OPENED); |
| 202 if (e != undefined) |
| 203 return e; |
| 204 // History items are never opened, so use the activation info |
| 205 e = this.findLogEntryByType_(EventType.DOWNLOAD_ITEM_ACTIVE); |
| 206 if (e != undefined) |
| 207 return e; |
| 208 } |
| 209 if (this.entries_.length >= 2) { |
| 210 // Needed for compatability with log dumps prior to M26. |
| 211 // TODO(mmenke): Remove this. |
| 212 if (this.entries_[0].type == EventType.SOCKET_POOL_CONNECT_JOB && |
| 213 this.entries_[0].params == undefined) { |
| 214 return this.entries_[1]; |
| 215 } |
| 216 if (this.entries_[1].type == EventType.UDP_CONNECT) |
| 217 return this.entries_[1]; |
| 218 if (this.entries_[0].type == EventType.REQUEST_ALIVE && |
| 219 this.entries_[0].params == undefined) { |
| 220 var startIndex = 1; |
| 221 // Skip over delegate events for URL_REQUESTs. |
| 222 for (; startIndex + 1 < this.entries_.length; ++startIndex) { |
| 223 var type = this.entries_[startIndex].type; |
| 224 if (type != EventType.URL_REQUEST_DELEGATE && |
| 225 type != EventType.DELEGATE_INFO) { |
| 226 break; |
| 227 } |
| 228 } |
| 229 return this.entries_[startIndex]; |
| 230 } |
| 231 if (this.entries_[1].type == EventType.IPV6_PROBE_RUNNING) |
| 232 return this.entries_[1]; |
| 233 } |
| 234 return this.entries_[0]; |
| 235 }, |
| 236 |
| 237 /** |
| 238 * Returns the first entry with the specified type, or undefined if not |
| 239 * found. |
| 240 */ |
| 241 findLogEntryByType_: function(type) { |
| 242 for (var i = 0; i < this.entries_.length; ++i) { |
| 243 if (this.entries_[i].type == type) { |
| 244 return this.entries_[i]; |
| 245 } |
| 246 } |
| 247 return undefined; |
| 248 }, |
| 249 |
| 250 /** |
| 251 * Returns the beginning of the last entry with the specified type, or |
| 252 * undefined if not found. |
| 253 */ |
| 254 findLastLogEntryStartByType_: function(type) { |
| 255 for (var i = this.entries_.length - 1; i >= 0; --i) { |
| 256 if (this.entries_[i].type == type) { |
| 257 if (this.entries_[i].phase != EventPhase.PHASE_END) |
| 258 return this.entries_[i]; |
| 259 } |
| 260 } |
| 261 return undefined; |
| 262 }, |
| 263 |
| 264 getLogEntries: function() { |
| 265 return this.entries_; |
| 266 }, |
| 267 |
| 268 getSourceTypeString: function() { |
| 269 return EventSourceTypeNames[this.entries_[0].source.type]; |
| 270 }, |
| 271 |
| 272 getSourceType: function() { |
| 273 return this.entries_[0].source.type; |
| 274 }, |
| 275 |
| 276 getSourceId: function() { |
| 277 return this.entries_[0].source.id; |
| 278 }, |
| 279 |
| 280 /** |
| 281 * Returns the largest source ID seen before this object was received. |
| 282 * Used only for sorting SourceEntries without a source by source ID. |
| 283 */ |
| 284 getMaxPreviousEntrySourceId: function() { |
| 285 return this.maxPreviousSourceId_; |
| 286 }, |
| 287 |
| 288 isInactive: function() { |
| 289 return this.isInactive_; |
| 290 }, |
| 291 |
| 292 isError: function() { |
| 293 return this.isError_; |
| 294 }, |
| 295 |
| 296 /** |
| 297 * Returns time ticks of first event. |
| 298 */ |
| 299 getStartTicks: function() { |
| 300 return this.entries_[0].time; |
| 301 }, |
| 302 |
| 303 /** |
| 304 * Returns time of last event if inactive. Returns current time otherwise. |
| 305 * Returned time is a "time ticks" value. |
| 306 */ |
| 307 getEndTicks: function() { |
| 308 if (!this.isInactive_) |
| 309 return timeutil.getCurrentTimeTicks(); |
| 310 return this.entries_[this.entries_.length - 1].time; |
| 311 }, |
| 312 |
| 313 /** |
| 314 * Returns the time between the first and last events with a matching |
| 315 * source ID. If source is still active, uses the current time for the |
| 316 * last event. |
| 317 */ |
| 318 getDuration: function() { |
| 319 var startTime = this.getStartTicks(); |
| 320 var endTime = this.getEndTicks(); |
| 321 return endTime - startTime; |
| 322 }, |
| 323 |
| 324 /** |
| 325 * Prints descriptive text about |entries_| to a new node added to the end |
| 326 * of |parent|. |
| 327 */ |
| 328 printAsText: function(parent) { |
| 329 var tablePrinter = this.createTablePrinter(); |
| 330 |
| 331 // Format the table for fixed-width text. |
| 332 tablePrinter.toText(0, parent); |
| 333 }, |
| 334 |
| 335 /** |
| 336 * Creates a table printer for the SourceEntry. |
| 337 */ |
| 338 createTablePrinter: function() { |
| 339 return createLogEntryTablePrinter( |
| 340 this.entries_, |
| 341 SourceTracker.getInstance().getPrivacyStripping(), |
| 342 SourceTracker.getInstance().getUseRelativeTimes() ? |
| 343 timeutil.getBaseTime() : 0, |
| 344 Constants.clientInfo.numericDate); |
| 345 }, |
| 346 }; |
| 347 |
| 348 return SourceEntry; |
| 349 })(); |
| 350 |
OLD | NEW |